From 711eb998125ceaf23237f9a2eef2b90086f4e48a Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 24 Jun 2016 12:37:31 +0200 Subject: [PATCH] HibernateJpaVendorAdapter and LocalSessionFactoryBuilder enforce Hibernate 5.2's connection handling mode DELAYED_ACQUISITION_AND_HOLD Issue: SPR-14393 --- .../HibernateTransactionManager.java | 2 -- .../LocalSessionFactoryBuilder.java | 9 +++++++ .../orm/jpa/vendor/HibernateJpaDialect.java | 16 +++--------- .../jpa/vendor/HibernateJpaVendorAdapter.java | 26 +++++++++++++++++++ 4 files changed, 38 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 93c815bb46..7fdf0e2352 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 @@ -235,7 +235,6 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana *

Default is "false". Turning this flag on enforces over-commit holdability on the * underlying JDBC Connection (if {@link #prepareConnection "prepareConnection"} is on) * and skips the disconnect-on-completion step. - * @since 4.2 * @see Connection#setHoldability * @see ResultSet#HOLD_CURSORS_OVER_COMMIT * @see #disconnectOnCompletion(Session) @@ -689,7 +688,6 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana *

The default implementation simply calls {@link Session#disconnect()}. * Subclasses may override this with a no-op or with fine-tuned disconnection logic. * @param session the Hibernate Session to disconnect - * @since 4.2 * @see Session#disconnect() */ protected void disconnectOnCompletion(Session session) { diff --git a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/LocalSessionFactoryBuilder.java b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/LocalSessionFactoryBuilder.java index a24370743d..066fc4511e 100644 --- a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/LocalSessionFactoryBuilder.java +++ b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/LocalSessionFactoryBuilder.java @@ -139,6 +139,10 @@ public class LocalSessionFactoryBuilder extends Configuration { if (dataSource != null) { getProperties().put(Environment.DATASOURCE, dataSource); } + + // Hibernate 5.2: manually enforce connection release mode ON_CLOSE (the former default) + getProperties().put("hibernate.connection.handling_mode", "DELAYED_ACQUISITION_AND_HOLD"); + getProperties().put(AvailableSettings.CLASSLOADERS, Collections.singleton(resourceLoader.getClassLoader())); this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader); } @@ -157,6 +161,7 @@ public class LocalSessionFactoryBuilder extends Configuration { */ public LocalSessionFactoryBuilder setJtaTransactionManager(Object jtaTransactionManager) { Assert.notNull(jtaTransactionManager, "Transaction manager reference must not be null"); + if (jtaTransactionManager instanceof JtaTransactionManager) { boolean webspherePresent = ClassUtils.isPresent("com.ibm.wsspi.uow.UOWManager", getClass().getClassLoader()); if (webspherePresent) { @@ -182,6 +187,10 @@ public class LocalSessionFactoryBuilder extends Configuration { throw new IllegalArgumentException( "Unknown transaction manager type: " + jtaTransactionManager.getClass().getName()); } + + // Hibernate 5.2: manually enforce connection release mode AFTER_STATEMENT (the JTA default) + getProperties().remove("hibernate.connection.handling_mode", "DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT"); + return this; } 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 e8ed961769..c089b42561 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 @@ -126,7 +126,7 @@ public class HibernateJpaDialect extends DefaultJpaDialect { } - private boolean prepareConnection = (HibernateConnectionHandle.sessionConnectionMethod == null); + boolean prepareConnection = (HibernateConnectionHandle.sessionConnectionMethod == null); /** @@ -362,25 +362,15 @@ public class HibernateJpaDialect extends DefaultJpaDialect { } if (this.preparedCon != null && this.session.isConnected()) { Connection conToReset = HibernateConnectionHandle.doGetConnection(this.session); - if (!isEquivalentConnection(conToReset)) { + if (conToReset != this.preparedCon) { LogFactory.getLog(HibernateJpaDialect.class).warn( - "JDBC Connection to reset not equivalent to originally prepared Connection - please " + + "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); } } - - private boolean isEquivalentConnection(Connection con) { - try { - return (con.equals(this.preparedCon) || - con.unwrap(Connection.class).equals(this.preparedCon.unwrap(Connection.class))); - } - catch (Throwable ex) { - return false; - } - } } diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/HibernateJpaVendorAdapter.java b/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/HibernateJpaVendorAdapter.java index 108eea1319..4451a8bf9a 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/HibernateJpaVendorAdapter.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/HibernateJpaVendorAdapter.java @@ -101,6 +101,27 @@ public class HibernateJpaVendorAdapter extends AbstractJpaVendorAdapter { } + /** + * Set whether to prepare the underlying JDBC Connection of a transactional + * Hibernate Session, that is, whether to apply a transaction-specific + * isolation level and/or the transaction's read-only flag to the underlying + * JDBC Connection. + *

See {@link HibernateJpaDialect#setPrepareConnection(boolean)} for details. + * This is just a convenience flag passed through to {@code HibernateJpaDialect}. + *

On Hibernate 5.2, this flag remains {@code true} by default like against + * previous Hibernate versions. The vendor adapter manually enforces Hibernate's + * new connection handling mode {@code DELAYED_ACQUISITION_AND_HOLD} in that case + * unless a user-specified connection handling mode property indicates otherwise; + * switch this flag to {@code false} to avoid that interference. + * @since 4.3.1 + * @see #getJpaPropertyMap() + * @see HibernateJpaDialect#beginTransaction + */ + public void setPrepareConnection(boolean prepareConnection) { + this.jpaDialect.setPrepareConnection(prepareConnection); + } + + @Override public PersistenceProvider getPersistenceProvider() { return this.persistenceProvider; @@ -132,6 +153,11 @@ public class HibernateJpaVendorAdapter extends AbstractJpaVendorAdapter { jpaProperties.put(Environment.SHOW_SQL, "true"); } + if (this.jpaDialect.prepareConnection) { + // Hibernate 5.2: manually enforce connection release mode ON_CLOSE (the former default) + jpaProperties.put("hibernate.connection.handling_mode", "DELAYED_ACQUISITION_AND_HOLD"); + } + return jpaProperties; }