From a1107af06f592f35d0e17e0f24041d5d6eb01cd3 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 27 Aug 2015 16:41:41 +0200 Subject: [PATCH] HibernateJpaDialect logs warning in case of Connection mismatch (e.g. configured release mode other than ON_CLOSE) Related to that, HibernateTransactionManager specifically checks for active physical connections on reset as of Hibernate 5. Issue: SPR-13269 Issue: SPR-13002 --- .../HibernateTransactionManager.java | 25 ++++++++++++++-- .../orm/jpa/vendor/HibernateJpaDialect.java | 30 +++++++++++-------- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTransactionManager.java b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTransactionManager.java index 280cc44a431..d26f18b8053 100644 --- a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTransactionManager.java +++ b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTransactionManager.java @@ -641,7 +641,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana } Session session = txObject.getSessionHolder().getSession(); - if (this.prepareConnection && session.isConnected() && isSameConnectionForEntireSession(session)) { + if (this.prepareConnection && isPhysicallyConnected(session)) { // We're running with connection release mode "on_close": We're able to reset // the isolation level and/or read-only flag of the JDBC Connection here. // Else, we need to rely on the connection pool to perform proper cleanup. @@ -704,8 +704,27 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana * @see ConnectionReleaseMode#ON_CLOSE */ protected boolean isSameConnectionForEntireSession(Session session) { - // TODO: The best we can do is to assume we're safe. - return true; + if (!(session instanceof SessionImplementor)) { + // The best we can do is to assume we're safe. + return true; + } + ConnectionReleaseMode releaseMode = + ((SessionImplementor) session).getJdbcCoordinator().getConnectionReleaseMode(); + return ConnectionReleaseMode.ON_CLOSE.equals(releaseMode); + } + + /** + * Determine whether the given Session is (still) physically connected + * to the database, that is, holds an active JDBC Connection internally. + * @param session the Hibernate Session to check + * @see #isSameConnectionForEntireSession(Session) + */ + protected boolean isPhysicallyConnected(Session session) { + if (!(session instanceof SessionImplementor)) { + // The best we can do is to check whether we're logically connected. + return session.isConnected(); + } + return ((SessionImplementor) session).getJdbcCoordinator().getLogicalConnection().isPhysicallyConnected(); } diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/HibernateJpaDialect.java b/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/HibernateJpaDialect.java index b422c8745c5..3de8631b787 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/HibernateJpaDialect.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/HibernateJpaDialect.java @@ -22,6 +22,7 @@ import java.sql.SQLException; import javax.persistence.EntityManager; import javax.persistence.PersistenceException; +import org.apache.commons.logging.LogFactory; import org.hibernate.FlushMode; import org.hibernate.HibernateException; import org.hibernate.NonUniqueObjectException; @@ -146,13 +147,12 @@ public class HibernateJpaDialect extends DefaultJpaDialect { boolean isolationLevelNeeded = (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT); Integer previousIsolationLevel = null; - boolean resetConnection = false; + Connection preparedCon = null; if (isolationLevelNeeded || definition.isReadOnly()) { if (this.prepareConnection) { - Connection con = HibernateConnectionHandle.doGetConnection(session); - previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition); - resetConnection = true; + preparedCon = HibernateConnectionHandle.doGetConnection(session); + previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(preparedCon, definition); } else if (isolationLevelNeeded) { throw new InvalidIsolationLevelException(getClass().getSimpleName() + @@ -167,7 +167,7 @@ public class HibernateJpaDialect extends DefaultJpaDialect { // Adapt flush mode and store previous isolation level, if any. FlushMode previousFlushMode = prepareFlushMode(session, definition.isReadOnly()); - return new SessionTransactionData(session, previousFlushMode, resetConnection, previousIsolationLevel); + return new SessionTransactionData(session, previousFlushMode, preparedCon, previousIsolationLevel); } @Override @@ -176,7 +176,7 @@ public class HibernateJpaDialect extends DefaultJpaDialect { Session session = getSession(entityManager); FlushMode previousFlushMode = prepareFlushMode(session, readOnly); - return new SessionTransactionData(session, previousFlushMode, false, null); + return new SessionTransactionData(session, previousFlushMode, null, null); } protected FlushMode prepareFlushMode(Session session, boolean readOnly) throws PersistenceException { @@ -321,15 +321,15 @@ public class HibernateJpaDialect extends DefaultJpaDialect { private final FlushMode previousFlushMode; - private final boolean resetConnection; + private final Connection preparedCon; private final Integer previousIsolationLevel; public SessionTransactionData( - Session session, FlushMode previousFlushMode, boolean resetConnection, Integer previousIsolationLevel) { + Session session, FlushMode previousFlushMode, Connection preparedCon, Integer previousIsolationLevel) { this.session = session; this.previousFlushMode = previousFlushMode; - this.resetConnection = resetConnection; + this.preparedCon = preparedCon; this.previousIsolationLevel = previousIsolationLevel; } @@ -337,9 +337,15 @@ public class HibernateJpaDialect extends DefaultJpaDialect { if (this.previousFlushMode != null) { this.session.setFlushMode(this.previousFlushMode); } - if (this.resetConnection && this.session.isConnected()) { - Connection con = HibernateConnectionHandle.doGetConnection(this.session); - DataSourceUtils.resetConnectionAfterTransaction(con, this.previousIsolationLevel); + if (this.preparedCon != null && this.session.isConnected()) { + Connection conToReset = HibernateConnectionHandle.doGetConnection(this.session); + if (conToReset != this.preparedCon) { + LogFactory.getLog(HibernateJpaDialect.class).warn( + "JDBC Connection to reset not identical to originally prepared Connection - please " + + "make sure to use connection release mode ON_CLOSE (the default) and to run against " + + "Hibernate 4.2+ (or switch HibernateJpaDialect's prepareConnection flag to false"); + } + DataSourceUtils.resetConnectionAfterTransaction(conToReset, this.previousIsolationLevel); } } }