HibernateJpaDialect prepares JDBC Connection by default if on Hibernate EntityManager 4 (with its connection release mode ON_CLOSE)
Analogous to HibernateTransactionManager, there is a "prepareConnection" flag on HibernateJpaDialect which allows for overriding the actual mode of operation. This is easily accessible from HibernateJpaVendorAdapter now which declares HibernateJpaDialect from its getJpaDialect() method. Issue: SPR-8959 Issue: SPR-11942
This commit is contained in:
		
							parent
							
								
									e08c56fcae
								
							
						
					
					
						commit
						cbda722329
					
				|  | @ -91,6 +91,9 @@ import org.springframework.transaction.support.TransactionSynchronizationManager | ||||||
|  * support nested transactions! Hence, do not expect Hibernate access code to |  * support nested transactions! Hence, do not expect Hibernate access code to | ||||||
|  * semantically participate in a nested transaction.</i> |  * semantically participate in a nested transaction.</i> | ||||||
|  * |  * | ||||||
|  |  * <p><b>NOTE: Hibernate 4.2+ is strongly recommended for efficient transaction | ||||||
|  |  * management with Spring, in particular for transactional Spring JDBC access.</b> | ||||||
|  |  * | ||||||
|  * @author Juergen Hoeller |  * @author Juergen Hoeller | ||||||
|  * @since 3.1 |  * @since 3.1 | ||||||
|  * @see #setSessionFactory |  * @see #setSessionFactory | ||||||
|  | @ -437,7 +440,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana | ||||||
| 					throw new InvalidIsolationLevelException( | 					throw new InvalidIsolationLevelException( | ||||||
| 							"HibernateTransactionManager is not allowed to support custom isolation levels: " + | 							"HibernateTransactionManager is not allowed to support custom isolation levels: " + | ||||||
| 							"make sure that its 'prepareConnection' flag is on (the default) and that the " + | 							"make sure that its 'prepareConnection' flag is on (the default) and that the " + | ||||||
| 							"Hibernate connection release mode is set to 'on_close' (SpringTransactionFactory's default)."); | 							"Hibernate connection release mode is set to 'on_close' (the default for JDBC)."); | ||||||
| 				} | 				} | ||||||
| 				if (logger.isDebugEnabled()) { | 				if (logger.isDebugEnabled()) { | ||||||
| 					logger.debug("Not preparing JDBC Connection of Hibernate Session [" + session + "]"); | 					logger.debug("Not preparing JDBC Connection of Hibernate Session [" + session + "]"); | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| /* | /* | ||||||
|  * Copyright 2002-2013 the original author or authors. |  * Copyright 2002-2014 the original author or authors. | ||||||
|  * |  * | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  * you may not use this file except in compliance with the License. |  * you may not use this file except in compliance with the License. | ||||||
|  | @ -55,12 +55,14 @@ import org.springframework.dao.InvalidDataAccessApiUsageException; | ||||||
| import org.springframework.dao.InvalidDataAccessResourceUsageException; | import org.springframework.dao.InvalidDataAccessResourceUsageException; | ||||||
| import org.springframework.dao.PessimisticLockingFailureException; | import org.springframework.dao.PessimisticLockingFailureException; | ||||||
| import org.springframework.jdbc.datasource.ConnectionHandle; | import org.springframework.jdbc.datasource.ConnectionHandle; | ||||||
|  | import org.springframework.jdbc.datasource.DataSourceUtils; | ||||||
| import org.springframework.jdbc.support.JdbcUtils; | import org.springframework.jdbc.support.JdbcUtils; | ||||||
| import org.springframework.orm.ObjectOptimisticLockingFailureException; | import org.springframework.orm.ObjectOptimisticLockingFailureException; | ||||||
| import org.springframework.orm.ObjectRetrievalFailureException; | import org.springframework.orm.ObjectRetrievalFailureException; | ||||||
| import org.springframework.orm.jpa.DefaultJpaDialect; | import org.springframework.orm.jpa.DefaultJpaDialect; | ||||||
| import org.springframework.orm.jpa.EntityManagerFactoryUtils; | import org.springframework.orm.jpa.EntityManagerFactoryUtils; | ||||||
| import org.springframework.orm.jpa.JpaSystemException; | import org.springframework.orm.jpa.JpaSystemException; | ||||||
|  | import org.springframework.transaction.InvalidIsolationLevelException; | ||||||
| import org.springframework.transaction.TransactionDefinition; | import org.springframework.transaction.TransactionDefinition; | ||||||
| import org.springframework.transaction.TransactionException; | import org.springframework.transaction.TransactionException; | ||||||
| import org.springframework.util.ClassUtils; | import org.springframework.util.ClassUtils; | ||||||
|  | @ -70,8 +72,8 @@ import org.springframework.util.ReflectionUtils; | ||||||
|  * {@link org.springframework.orm.jpa.JpaDialect} implementation for |  * {@link org.springframework.orm.jpa.JpaDialect} implementation for | ||||||
|  * Hibernate EntityManager. Developed against Hibernate 3.6 and 4.2/4.3. |  * Hibernate EntityManager. Developed against Hibernate 3.6 and 4.2/4.3. | ||||||
|  * |  * | ||||||
|  * @author Costin Leau |  | ||||||
|  * @author Juergen Hoeller |  * @author Juergen Hoeller | ||||||
|  |  * @author Costin Leau | ||||||
|  * @since 2.0 |  * @since 2.0 | ||||||
|  */ |  */ | ||||||
| @SuppressWarnings({"serial", "deprecation"}) | @SuppressWarnings({"serial", "deprecation"}) | ||||||
|  | @ -100,22 +102,73 @@ public class HibernateJpaDialect extends DefaultJpaDialect { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | 	private boolean prepareConnection = (HibernateConnectionHandle.sessionConnectionMethod == null); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * 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. | ||||||
|  | 	 * <p>Default is "true" on Hibernate EntityManager 4.x (with its 'on-close' | ||||||
|  | 	 * connection release mode, and "false" on Hibernate EntityManager 3.6 (due to | ||||||
|  | 	 * the 'after-transaction' release mode there). <b>Note that Hibernate 4.2+ is | ||||||
|  | 	 * strongly recommended in order to make isolation levels work efficiently.</b> | ||||||
|  | 	 * <p>If you turn this flag off, JPA transaction management will not support | ||||||
|  | 	 * per-transaction isolation levels anymore. It will not call | ||||||
|  | 	 * {@code Connection.setReadOnly(true)} for read-only transactions anymore either. | ||||||
|  | 	 * If this flag is turned off, no cleanup of a JDBC Connection is required after | ||||||
|  | 	 * a transaction, since no Connection settings will get modified. | ||||||
|  | 	 * @see java.sql.Connection#setTransactionIsolation | ||||||
|  | 	 * @see java.sql.Connection#setReadOnly | ||||||
|  | 	 */ | ||||||
|  | 	public void setPrepareConnection(boolean prepareConnection) { | ||||||
|  | 		this.prepareConnection = prepareConnection; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| 	@Override | 	@Override | ||||||
| 	public Object beginTransaction(EntityManager entityManager, TransactionDefinition definition) | 	public Object beginTransaction(EntityManager entityManager, TransactionDefinition definition) | ||||||
| 			throws PersistenceException, SQLException, TransactionException { | 			throws PersistenceException, SQLException, TransactionException { | ||||||
| 
 | 
 | ||||||
|  | 		Session session = getSession(entityManager); | ||||||
|  | 
 | ||||||
| 		if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) { | 		if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) { | ||||||
| 			getSession(entityManager).getTransaction().setTimeout(definition.getTimeout()); | 			session.getTransaction().setTimeout(definition.getTimeout()); | ||||||
| 		} | 		} | ||||||
| 		super.beginTransaction(entityManager, definition); | 
 | ||||||
| 		return prepareTransaction(entityManager, definition.isReadOnly(), definition.getName()); | 		Integer previousIsolationLevel = null; | ||||||
|  | 		boolean isolationLevelNeeded = (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT); | ||||||
|  | 		if (isolationLevelNeeded || definition.isReadOnly()) { | ||||||
|  | 			if (this.prepareConnection) { | ||||||
|  | 				Connection con = HibernateConnectionHandle.doGetConnection(session); | ||||||
|  | 				previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition); | ||||||
|  | 			} | ||||||
|  | 			else if (isolationLevelNeeded) { | ||||||
|  | 				throw new InvalidIsolationLevelException(getClass().getSimpleName() + | ||||||
|  | 						" does not support custom isolation levels since the 'prepareConnection' flag is off. " + | ||||||
|  | 						"This is the case on Hibernate 3.6 by default; either switch that flag at your own risk " + | ||||||
|  | 						"or upgrade to Hibernate 4.x, with 4.2+ recommended."); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Standard JPA transaction begin call for full JPA context setup... | ||||||
|  | 		entityManager.getTransaction().begin(); | ||||||
|  | 
 | ||||||
|  | 		// Adapt flush mode and store previous isolation level, if any. | ||||||
|  | 		return doPrepareTransaction(session, definition.isReadOnly(), previousIsolationLevel); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Override | 	@Override | ||||||
| 	public Object prepareTransaction(EntityManager entityManager, boolean readOnly, String name) | 	public Object prepareTransaction(EntityManager entityManager, boolean readOnly, String name) | ||||||
| 			throws PersistenceException { | 			throws PersistenceException { | ||||||
| 
 | 
 | ||||||
| 		Session session = getSession(entityManager); | 		return doPrepareTransaction(getSession(entityManager), readOnly, null); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	protected Object doPrepareTransaction(Session session, boolean readOnly, Integer previousIsolationLevel) | ||||||
|  | 			throws PersistenceException { | ||||||
|  | 
 | ||||||
| 		FlushMode flushMode = session.getFlushMode(); | 		FlushMode flushMode = session.getFlushMode(); | ||||||
| 		FlushMode previousFlushMode = null; | 		FlushMode previousFlushMode = null; | ||||||
| 		if (readOnly) { | 		if (readOnly) { | ||||||
|  | @ -130,12 +183,14 @@ public class HibernateJpaDialect extends DefaultJpaDialect { | ||||||
| 				previousFlushMode = flushMode; | 				previousFlushMode = flushMode; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		return new SessionTransactionData(session, previousFlushMode); | 
 | ||||||
|  | 		boolean resetConnection = (previousIsolationLevel != null || readOnly); | ||||||
|  | 		return new SessionTransactionData(session, previousFlushMode, resetConnection, previousIsolationLevel); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Override | 	@Override | ||||||
| 	public void cleanupTransaction(Object transactionData) { | 	public void cleanupTransaction(Object transactionData) { | ||||||
| 		((SessionTransactionData) transactionData).resetFlushMode(); | 		((SessionTransactionData) transactionData).resetSessionState(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Override | 	@Override | ||||||
|  | @ -255,15 +310,26 @@ public class HibernateJpaDialect extends DefaultJpaDialect { | ||||||
| 
 | 
 | ||||||
| 		private final FlushMode previousFlushMode; | 		private final FlushMode previousFlushMode; | ||||||
| 
 | 
 | ||||||
| 		public SessionTransactionData(Session session, FlushMode previousFlushMode) { | 		private final boolean connectionReset; | ||||||
|  | 
 | ||||||
|  | 		private final Integer previousIsolationLevel; | ||||||
|  | 
 | ||||||
|  | 		public SessionTransactionData( | ||||||
|  | 				Session session, FlushMode previousFlushMode, boolean resetConnection, Integer previousIsolationLevel) { | ||||||
| 			this.session = session; | 			this.session = session; | ||||||
| 			this.previousFlushMode = previousFlushMode; | 			this.previousFlushMode = previousFlushMode; | ||||||
|  | 			this.connectionReset = resetConnection; | ||||||
|  | 			this.previousIsolationLevel = previousIsolationLevel; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		public void resetFlushMode() { | 		public void resetSessionState() { | ||||||
| 			if (this.previousFlushMode != null) { | 			if (this.previousFlushMode != null) { | ||||||
| 				this.session.setFlushMode(this.previousFlushMode); | 				this.session.setFlushMode(this.previousFlushMode); | ||||||
| 			} | 			} | ||||||
|  | 			if (this.connectionReset && this.session.isConnected()) { | ||||||
|  | 				Connection con = HibernateConnectionHandle.doGetConnection(this.session); | ||||||
|  | 				DataSourceUtils.resetConnectionAfterTransaction(con, this.previousIsolationLevel); | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -284,16 +350,7 @@ public class HibernateJpaDialect extends DefaultJpaDialect { | ||||||
| 
 | 
 | ||||||
| 		@Override | 		@Override | ||||||
| 		public Connection getConnection() { | 		public Connection getConnection() { | ||||||
| 			try { | 			return doGetConnection(this.session); | ||||||
| 				if (connectionMethodToUse == null) { |  | ||||||
| 					// Reflective lookup trying to find SessionImpl's connection() on Hibernate 4.x |  | ||||||
| 					connectionMethodToUse = this.session.getClass().getMethod("connection"); |  | ||||||
| 				} |  | ||||||
| 				return (Connection) ReflectionUtils.invokeMethod(connectionMethodToUse, this.session); |  | ||||||
| 			} |  | ||||||
| 			catch (NoSuchMethodException ex) { |  | ||||||
| 				throw new IllegalStateException("Cannot find connection() method on Hibernate Session", ex); |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		@Override | 		@Override | ||||||
|  | @ -307,6 +364,19 @@ public class HibernateJpaDialect extends DefaultJpaDialect { | ||||||
| 				JdbcUtils.closeConnection(con); | 				JdbcUtils.closeConnection(con); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 		public static Connection doGetConnection(Session session) { | ||||||
|  | 			try { | ||||||
|  | 				if (connectionMethodToUse == null) { | ||||||
|  | 					// Reflective lookup trying to find SessionImpl's connection() on Hibernate 4.x | ||||||
|  | 					connectionMethodToUse = session.getClass().getMethod("connection"); | ||||||
|  | 				} | ||||||
|  | 				return (Connection) ReflectionUtils.invokeMethod(connectionMethodToUse, session); | ||||||
|  | 			} | ||||||
|  | 			catch (NoSuchMethodException ex) { | ||||||
|  | 				throw new IllegalStateException("Cannot find connection() method on Hibernate Session", ex); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -33,11 +33,10 @@ import org.hibernate.dialect.Oracle9iDialect; | ||||||
| import org.hibernate.dialect.PostgreSQLDialect; | import org.hibernate.dialect.PostgreSQLDialect; | ||||||
| import org.hibernate.dialect.SQLServerDialect; | import org.hibernate.dialect.SQLServerDialect; | ||||||
| 
 | 
 | ||||||
| import org.springframework.orm.jpa.JpaDialect; |  | ||||||
| 
 |  | ||||||
| /** | /** | ||||||
|  * {@link org.springframework.orm.jpa.JpaVendorAdapter} implementation for |  * {@link org.springframework.orm.jpa.JpaVendorAdapter} implementation for | ||||||
|  * Hibernate EntityManager. Developed and tested against Hibernate 3.6 and 4.2/4.3. |  * Hibernate EntityManager. Developed and tested against Hibernate 3.6 and 4.2/4.3. | ||||||
|  |  * <b>Hibernate 4.2+ is strongly recommended for use with Spring 4.0+.</b> | ||||||
|  * |  * | ||||||
|  * <p>Exposes Hibernate's persistence provider and EntityManager extension interface, |  * <p>Exposes Hibernate's persistence provider and EntityManager extension interface, | ||||||
|  * and adapts {@link AbstractJpaVendorAdapter}'s common configuration settings. |  * and adapts {@link AbstractJpaVendorAdapter}'s common configuration settings. | ||||||
|  | @ -62,7 +61,7 @@ import org.springframework.orm.jpa.JpaDialect; | ||||||
|  */ |  */ | ||||||
| public class HibernateJpaVendorAdapter extends AbstractJpaVendorAdapter { | public class HibernateJpaVendorAdapter extends AbstractJpaVendorAdapter { | ||||||
| 
 | 
 | ||||||
| 	private final JpaDialect jpaDialect = new HibernateJpaDialect(); | 	private final HibernateJpaDialect jpaDialect = new HibernateJpaDialect(); | ||||||
| 
 | 
 | ||||||
| 	private final PersistenceProvider persistenceProvider; | 	private final PersistenceProvider persistenceProvider; | ||||||
| 
 | 
 | ||||||
|  | @ -159,7 +158,7 @@ public class HibernateJpaVendorAdapter extends AbstractJpaVendorAdapter { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Override | 	@Override | ||||||
| 	public JpaDialect getJpaDialect() { | 	public HibernateJpaDialect getJpaDialect() { | ||||||
| 		return this.jpaDialect; | 		return this.jpaDialect; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue