Skip to content

Commit a1107af

Browse files
committed
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
1 parent e553bb7 commit a1107af

File tree

2 files changed

+40
-15
lines changed

2 files changed

+40
-15
lines changed

spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTransactionManager.java

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,7 @@ protected void doCleanupAfterCompletion(Object transaction) {
641641
}
642642

643643
Session session = txObject.getSessionHolder().getSession();
644-
if (this.prepareConnection && session.isConnected() && isSameConnectionForEntireSession(session)) {
644+
if (this.prepareConnection && isPhysicallyConnected(session)) {
645645
// We're running with connection release mode "on_close": We're able to reset
646646
// the isolation level and/or read-only flag of the JDBC Connection here.
647647
// Else, we need to rely on the connection pool to perform proper cleanup.
@@ -704,8 +704,27 @@ protected void disconnectOnCompletion(Session session) {
704704
* @see ConnectionReleaseMode#ON_CLOSE
705705
*/
706706
protected boolean isSameConnectionForEntireSession(Session session) {
707-
// TODO: The best we can do is to assume we're safe.
708-
return true;
707+
if (!(session instanceof SessionImplementor)) {
708+
// The best we can do is to assume we're safe.
709+
return true;
710+
}
711+
ConnectionReleaseMode releaseMode =
712+
((SessionImplementor) session).getJdbcCoordinator().getConnectionReleaseMode();
713+
return ConnectionReleaseMode.ON_CLOSE.equals(releaseMode);
714+
}
715+
716+
/**
717+
* Determine whether the given Session is (still) physically connected
718+
* to the database, that is, holds an active JDBC Connection internally.
719+
* @param session the Hibernate Session to check
720+
* @see #isSameConnectionForEntireSession(Session)
721+
*/
722+
protected boolean isPhysicallyConnected(Session session) {
723+
if (!(session instanceof SessionImplementor)) {
724+
// The best we can do is to check whether we're logically connected.
725+
return session.isConnected();
726+
}
727+
return ((SessionImplementor) session).getJdbcCoordinator().getLogicalConnection().isPhysicallyConnected();
709728
}
710729

711730

spring-orm/src/main/java/org/springframework/orm/jpa/vendor/HibernateJpaDialect.java

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import javax.persistence.EntityManager;
2323
import javax.persistence.PersistenceException;
2424

25+
import org.apache.commons.logging.LogFactory;
2526
import org.hibernate.FlushMode;
2627
import org.hibernate.HibernateException;
2728
import org.hibernate.NonUniqueObjectException;
@@ -146,13 +147,12 @@ public Object beginTransaction(EntityManager entityManager, TransactionDefinitio
146147

147148
boolean isolationLevelNeeded = (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT);
148149
Integer previousIsolationLevel = null;
149-
boolean resetConnection = false;
150+
Connection preparedCon = null;
150151

151152
if (isolationLevelNeeded || definition.isReadOnly()) {
152153
if (this.prepareConnection) {
153-
Connection con = HibernateConnectionHandle.doGetConnection(session);
154-
previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
155-
resetConnection = true;
154+
preparedCon = HibernateConnectionHandle.doGetConnection(session);
155+
previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(preparedCon, definition);
156156
}
157157
else if (isolationLevelNeeded) {
158158
throw new InvalidIsolationLevelException(getClass().getSimpleName() +
@@ -167,7 +167,7 @@ else if (isolationLevelNeeded) {
167167

168168
// Adapt flush mode and store previous isolation level, if any.
169169
FlushMode previousFlushMode = prepareFlushMode(session, definition.isReadOnly());
170-
return new SessionTransactionData(session, previousFlushMode, resetConnection, previousIsolationLevel);
170+
return new SessionTransactionData(session, previousFlushMode, preparedCon, previousIsolationLevel);
171171
}
172172

173173
@Override
@@ -176,7 +176,7 @@ public Object prepareTransaction(EntityManager entityManager, boolean readOnly,
176176

177177
Session session = getSession(entityManager);
178178
FlushMode previousFlushMode = prepareFlushMode(session, readOnly);
179-
return new SessionTransactionData(session, previousFlushMode, false, null);
179+
return new SessionTransactionData(session, previousFlushMode, null, null);
180180
}
181181

182182
protected FlushMode prepareFlushMode(Session session, boolean readOnly) throws PersistenceException {
@@ -321,25 +321,31 @@ private static class SessionTransactionData {
321321

322322
private final FlushMode previousFlushMode;
323323

324-
private final boolean resetConnection;
324+
private final Connection preparedCon;
325325

326326
private final Integer previousIsolationLevel;
327327

328328
public SessionTransactionData(
329-
Session session, FlushMode previousFlushMode, boolean resetConnection, Integer previousIsolationLevel) {
329+
Session session, FlushMode previousFlushMode, Connection preparedCon, Integer previousIsolationLevel) {
330330
this.session = session;
331331
this.previousFlushMode = previousFlushMode;
332-
this.resetConnection = resetConnection;
332+
this.preparedCon = preparedCon;
333333
this.previousIsolationLevel = previousIsolationLevel;
334334
}
335335

336336
public void resetSessionState() {
337337
if (this.previousFlushMode != null) {
338338
this.session.setFlushMode(this.previousFlushMode);
339339
}
340-
if (this.resetConnection && this.session.isConnected()) {
341-
Connection con = HibernateConnectionHandle.doGetConnection(this.session);
342-
DataSourceUtils.resetConnectionAfterTransaction(con, this.previousIsolationLevel);
340+
if (this.preparedCon != null && this.session.isConnected()) {
341+
Connection conToReset = HibernateConnectionHandle.doGetConnection(this.session);
342+
if (conToReset != this.preparedCon) {
343+
LogFactory.getLog(HibernateJpaDialect.class).warn(
344+
"JDBC Connection to reset not identical to originally prepared Connection - please " +
345+
"make sure to use connection release mode ON_CLOSE (the default) and to run against " +
346+
"Hibernate 4.2+ (or switch HibernateJpaDialect's prepareConnection flag to false");
347+
}
348+
DataSourceUtils.resetConnectionAfterTransaction(conToReset, this.previousIsolationLevel);
343349
}
344350
}
345351
}

0 commit comments

Comments
 (0)