Remove reflection usage in orm.hibernate4.*
Previously reflection was required when interacting with Hibernate 4 in order to support both Hibernate 3 and Hibernate 4 since there were non-passive changes in the APIs. Now that the Spring build uses Gradle it is trivial to support multiple Hibernate versions. This commit removes the reflection usage in orm.hibernate4.* by creating a spring-orm-hibernate4 module that uses gradle/merge-artifacts.gradle to build a single artifact but keep distinct classpaths. Issue: SPR-10039
This commit is contained in:
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.orm.hibernate4;
|
||||
|
||||
import javax.transaction.TransactionManager;
|
||||
import javax.transaction.UserTransaction;
|
||||
|
||||
import org.hibernate.service.jta.platform.internal.AbstractJtaPlatform;
|
||||
|
||||
import org.springframework.transaction.jta.UserTransactionAdapter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implementation of Hibernate 4's {@link org.hibernate.service.jta.platform.spi.JtaPlatform}
|
||||
* SPI, exposing passed-in {@link TransactionManager} and {@link UserTransaction} references.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1.2
|
||||
*/
|
||||
class ConfigurableJtaPlatform extends AbstractJtaPlatform {
|
||||
|
||||
private final TransactionManager transactionManager;
|
||||
|
||||
private final UserTransaction userTransaction;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new ConfigurableJtaPlatform instance with the given
|
||||
* JTA TransactionManager and optionally a given UserTransaction.
|
||||
* @param tm the JTA TransactionManager reference (required)
|
||||
* @param ut the JTA UserTransaction reference (optional)
|
||||
*/
|
||||
public ConfigurableJtaPlatform(TransactionManager tm, UserTransaction ut) {
|
||||
Assert.notNull(tm, "TransactionManager reference must not be null");
|
||||
this.transactionManager = tm;
|
||||
this.userTransaction = (ut != null ? ut : new UserTransactionAdapter(tm));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected TransactionManager locateTransactionManager() {
|
||||
return this.transactionManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UserTransaction locateUserTransaction() {
|
||||
return this.userTransaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canCacheTransactionManager() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canCacheUserTransaction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.orm.hibernate4;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
|
||||
/**
|
||||
* {@link PersistenceExceptionTranslator} capable of translating {@link HibernateException}
|
||||
* instances to Spring's {@link DataAccessException} hierarchy.
|
||||
*
|
||||
* <p>Extended by {@link LocalSessionFactoryBean}, so there is no need to declare this
|
||||
* translator in addition to a {@code LocalSessionFactoryBean}.
|
||||
*
|
||||
* <p>When configuring the container with {@code @Configuration} classes, a {@code @Bean}
|
||||
* of this type must be registered manually.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor
|
||||
* @see SessionFactoryUtils#convertHibernateAccessException(HibernateException)
|
||||
*/
|
||||
public class HibernateExceptionTranslator implements PersistenceExceptionTranslator {
|
||||
|
||||
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
|
||||
if (ex instanceof HibernateException) {
|
||||
return convertHibernateAccessException((HibernateException) ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given HibernateException to an appropriate exception from the
|
||||
* {@code org.springframework.dao} hierarchy.
|
||||
* @param ex HibernateException that occured
|
||||
* @return a corresponding DataAccessException
|
||||
* @see SessionFactoryUtils#convertHibernateAccessException
|
||||
*/
|
||||
protected DataAccessException convertHibernateAccessException(HibernateException ex) {
|
||||
return SessionFactoryUtils.convertHibernateAccessException(ex);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.orm.hibernate4;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.hibernate.JDBCException;
|
||||
|
||||
import org.springframework.dao.UncategorizedDataAccessException;
|
||||
|
||||
/**
|
||||
* Hibernate-specific subclass of UncategorizedDataAccessException,
|
||||
* for JDBC exceptions that Hibernate wrapped.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see SessionFactoryUtils#convertHibernateAccessException
|
||||
*/
|
||||
public class HibernateJdbcException extends UncategorizedDataAccessException {
|
||||
|
||||
public HibernateJdbcException(JDBCException ex) {
|
||||
super("JDBC exception on Hibernate data access: SQLException for SQL [" + ex.getSQL() + "]; SQL state [" +
|
||||
ex.getSQLState() + "]; error code [" + ex.getErrorCode() + "]; " + ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying SQLException.
|
||||
*/
|
||||
public SQLException getSQLException() {
|
||||
return ((JDBCException) getCause()).getSQLException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SQL that led to the problem.
|
||||
*/
|
||||
public String getSql() {
|
||||
return ((JDBCException) getCause()).getSQL();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.orm.hibernate4;
|
||||
|
||||
import org.hibernate.UnresolvableObjectException;
|
||||
import org.hibernate.WrongClassException;
|
||||
|
||||
import org.springframework.orm.ObjectRetrievalFailureException;
|
||||
|
||||
/**
|
||||
* Hibernate-specific subclass of ObjectRetrievalFailureException.
|
||||
* Converts Hibernate's UnresolvableObjectException and WrongClassException.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see SessionFactoryUtils#convertHibernateAccessException
|
||||
*/
|
||||
public class HibernateObjectRetrievalFailureException extends ObjectRetrievalFailureException {
|
||||
|
||||
public HibernateObjectRetrievalFailureException(UnresolvableObjectException ex) {
|
||||
super(ex.getEntityName(), ex.getIdentifier(), ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
public HibernateObjectRetrievalFailureException(WrongClassException ex) {
|
||||
super(ex.getEntityName(), ex.getIdentifier(), ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.orm.hibernate4;
|
||||
|
||||
import org.hibernate.StaleObjectStateException;
|
||||
import org.hibernate.StaleStateException;
|
||||
|
||||
import org.springframework.orm.ObjectOptimisticLockingFailureException;
|
||||
|
||||
/**
|
||||
* Hibernate-specific subclass of ObjectOptimisticLockingFailureException.
|
||||
* Converts Hibernate's StaleObjectStateException and StaleStateException.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see SessionFactoryUtils#convertHibernateAccessException
|
||||
*/
|
||||
public class HibernateOptimisticLockingFailureException extends ObjectOptimisticLockingFailureException {
|
||||
|
||||
public HibernateOptimisticLockingFailureException(StaleObjectStateException ex) {
|
||||
super(ex.getEntityName(), ex.getIdentifier(), ex);
|
||||
}
|
||||
|
||||
public HibernateOptimisticLockingFailureException(StaleStateException ex) {
|
||||
super(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.orm.hibernate4;
|
||||
|
||||
import org.hibernate.QueryException;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
|
||||
/**
|
||||
* Hibernate-specific subclass of InvalidDataAccessResourceUsageException,
|
||||
* thrown on invalid HQL query syntax.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see SessionFactoryUtils#convertHibernateAccessException
|
||||
*/
|
||||
public class HibernateQueryException extends InvalidDataAccessResourceUsageException {
|
||||
|
||||
public HibernateQueryException(QueryException ex) {
|
||||
super(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the HQL query string that was invalid.
|
||||
*/
|
||||
public String getQueryString() {
|
||||
return ((QueryException) getCause()).getQueryString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.orm.hibernate4;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
|
||||
import org.springframework.dao.UncategorizedDataAccessException;
|
||||
|
||||
/**
|
||||
* Hibernate-specific subclass of UncategorizedDataAccessException,
|
||||
* for Hibernate system errors that do not match any concrete
|
||||
* <code>org.springframework.dao</code> exceptions.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see SessionFactoryUtils#convertHibernateAccessException
|
||||
*/
|
||||
public class HibernateSystemException extends UncategorizedDataAccessException {
|
||||
|
||||
/**
|
||||
* Create a new HibernateSystemException,
|
||||
* wrapping an arbitrary HibernateException.
|
||||
* @param cause the HibernateException thrown
|
||||
*/
|
||||
public HibernateSystemException(HibernateException cause) {
|
||||
super(cause != null ? cause.getMessage() : null, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,708 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.orm.hibernate4;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.sql.Connection;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.hibernate.ConnectionReleaseMode;
|
||||
import org.hibernate.FlushMode;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.Transaction;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.engine.transaction.spi.TransactionContext;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.jdbc.datasource.ConnectionHolder;
|
||||
import org.springframework.jdbc.datasource.DataSourceUtils;
|
||||
import org.springframework.jdbc.datasource.JdbcTransactionObjectSupport;
|
||||
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
|
||||
import org.springframework.transaction.CannotCreateTransactionException;
|
||||
import org.springframework.transaction.IllegalTransactionStateException;
|
||||
import org.springframework.transaction.InvalidIsolationLevelException;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionSystemException;
|
||||
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
|
||||
import org.springframework.transaction.support.DefaultTransactionStatus;
|
||||
import org.springframework.transaction.support.ResourceTransactionManager;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.transaction.PlatformTransactionManager}
|
||||
* implementation for a single Hibernate {@link org.hibernate.SessionFactory}.
|
||||
* Binds a Hibernate Session from the specified factory to the thread,
|
||||
* potentially allowing for one thread-bound Session per factory.
|
||||
* <code>SessionFactory.getCurrentSession()</code> is required for Hibernate
|
||||
* access code that needs to support this transaction handling mechanism,
|
||||
* with the SessionFactory being configured with {@link SpringSessionContext}.
|
||||
*
|
||||
* <p>Supports custom isolation levels, and timeouts that get applied as
|
||||
* Hibernate transaction timeouts.
|
||||
*
|
||||
* <p>This transaction manager is appropriate for applications that use a single
|
||||
* Hibernate SessionFactory for transactional data access, but it also supports
|
||||
* direct DataSource access within a transaction (i.e. plain JDBC code working
|
||||
* with the same DataSource). This allows for mixing services which access Hibernate
|
||||
* and services which use plain JDBC (without being aware of Hibernate)!
|
||||
* Application code needs to stick to the same simple Connection lookup pattern as
|
||||
* with {@link org.springframework.jdbc.datasource.DataSourceTransactionManager}
|
||||
* (i.e. {@link org.springframework.jdbc.datasource.DataSourceUtils#getConnection}
|
||||
* or going through a
|
||||
* {@link org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy}).
|
||||
*
|
||||
* <p>Note: To be able to register a DataSource's Connection for plain JDBC code,
|
||||
* this instance needs to be aware of the DataSource ({@link #setDataSource}).
|
||||
* The given DataSource should obviously match the one used by the given SessionFactory.
|
||||
*
|
||||
* <p>JTA (usually through {@link org.springframework.transaction.jta.JtaTransactionManager})
|
||||
* is necessary for accessing multiple transactional resources within the same
|
||||
* transaction. The DataSource that Hibernate uses needs to be JTA-enabled in
|
||||
* such a scenario (see container setup).
|
||||
*
|
||||
* <p>On JDBC 3.0, this transaction manager supports nested transactions via JDBC 3.0
|
||||
* Savepoints. The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"}
|
||||
* flag defaults to "false", though, as nested transactions will just apply to the
|
||||
* JDBC Connection, not to the Hibernate Session and its cached objects. You can
|
||||
* manually set the flag to "true" if you want to use nested transactions for
|
||||
* JDBC access code which participates in Hibernate transactions (provided that
|
||||
* your JDBC driver supports Savepoints). <i>Note that Hibernate itself does not
|
||||
* support nested transactions! Hence, do not expect Hibernate access code to
|
||||
* semantically participate in a nested transaction.</i>
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see #setSessionFactory
|
||||
* @see #setDataSource
|
||||
* @see org.hibernate.SessionFactory#getCurrentSession()
|
||||
* @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection
|
||||
* @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
|
||||
* @see org.springframework.jdbc.core.JdbcTemplate
|
||||
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager
|
||||
* @see org.springframework.transaction.jta.JtaTransactionManager
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class HibernateTransactionManager extends AbstractPlatformTransactionManager
|
||||
implements ResourceTransactionManager, InitializingBean {
|
||||
|
||||
/**
|
||||
* A Method handle for the <code>SessionFactory.getCurrentSession()</code> method.
|
||||
* The return value differs between Hibernate 3.x and 4.x; for cross-compilation purposes,
|
||||
* we have to use reflection here as long as we keep compiling against Hibernate 3.x jars.
|
||||
*/
|
||||
private static final Method getCurrentSessionMethod =
|
||||
ClassUtils.getMethod(SessionFactory.class, "getCurrentSession");
|
||||
|
||||
|
||||
private SessionFactory sessionFactory;
|
||||
|
||||
private DataSource dataSource;
|
||||
|
||||
private boolean autodetectDataSource = true;
|
||||
|
||||
private boolean prepareConnection = true;
|
||||
|
||||
private boolean hibernateManagedSession = false;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new HibernateTransactionManager instance.
|
||||
* A SessionFactory has to be set to be able to use it.
|
||||
* @see #setSessionFactory
|
||||
*/
|
||||
public HibernateTransactionManager() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new HibernateTransactionManager instance.
|
||||
* @param sessionFactory SessionFactory to manage transactions for
|
||||
*/
|
||||
public HibernateTransactionManager(SessionFactory sessionFactory) {
|
||||
this.sessionFactory = sessionFactory;
|
||||
afterPropertiesSet();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the SessionFactory that this instance should manage transactions for.
|
||||
*/
|
||||
public void setSessionFactory(SessionFactory sessionFactory) {
|
||||
this.sessionFactory = sessionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SessionFactory that this instance should manage transactions for.
|
||||
*/
|
||||
public SessionFactory getSessionFactory() {
|
||||
return this.sessionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JDBC DataSource that this instance should manage transactions for.
|
||||
* The DataSource should match the one used by the Hibernate SessionFactory:
|
||||
* for example, you could specify the same JNDI DataSource for both.
|
||||
* <p>If the SessionFactory was configured with LocalDataSourceConnectionProvider,
|
||||
* i.e. by Spring's SessionFactoryBuilder with a specified "dataSource",
|
||||
* the DataSource will be auto-detected: You can still explictly specify the
|
||||
* DataSource, but you don't need to in this case.
|
||||
* <p>A transactional JDBC Connection for this DataSource will be provided to
|
||||
* application code accessing this DataSource directly via DataSourceUtils
|
||||
* or JdbcTemplate. The Connection will be taken from the Hibernate Session.
|
||||
* <p>The DataSource specified here should be the target DataSource to manage
|
||||
* transactions for, not a TransactionAwareDataSourceProxy. Only data access
|
||||
* code may work with TransactionAwareDataSourceProxy, while the transaction
|
||||
* manager needs to work on the underlying target DataSource. If there's
|
||||
* nevertheless a TransactionAwareDataSourceProxy passed in, it will be
|
||||
* unwrapped to extract its target DataSource.
|
||||
* @see #setAutodetectDataSource
|
||||
* @see org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy
|
||||
* @see org.springframework.jdbc.datasource.DataSourceUtils
|
||||
* @see org.springframework.jdbc.core.JdbcTemplate
|
||||
*/
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
if (dataSource instanceof TransactionAwareDataSourceProxy) {
|
||||
// If we got a TransactionAwareDataSourceProxy, we need to perform transactions
|
||||
// for its underlying target DataSource, else data access code won't see
|
||||
// properly exposed transactions (i.e. transactions for the target DataSource).
|
||||
this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();
|
||||
}
|
||||
else {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JDBC DataSource that this instance manages transactions for.
|
||||
*/
|
||||
public DataSource getDataSource() {
|
||||
return this.dataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to autodetect a JDBC DataSource used by the Hibernate SessionFactory,
|
||||
* if set via SessionFactoryBuilder's <code>setDataSource</code>. Default is "true".
|
||||
* <p>Can be turned off to deliberately ignore an available DataSource, in order
|
||||
* to not expose Hibernate transactions as JDBC transactions for that DataSource.
|
||||
* @see #setDataSource
|
||||
*/
|
||||
public void setAutodetectDataSource(boolean autodetectDataSource) {
|
||||
this.autodetectDataSource = autodetectDataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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". If you turn this flag off, the transaction manager
|
||||
* will not support per-transaction isolation levels anymore. It will not
|
||||
* call <code>Connection.setReadOnly(true)</code> 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to operate on a Hibernate-managed Session instead of a
|
||||
* Spring-managed Session, that is, whether to obtain the Session through
|
||||
* Hibernate's {@link org.hibernate.SessionFactory#getCurrentSession()}
|
||||
* instead of {@link org.hibernate.SessionFactory#openSession()} (with a Spring
|
||||
* {@link org.springframework.transaction.support.TransactionSynchronizationManager}
|
||||
* check preceding it).
|
||||
* <p>Default is "false", i.e. using a Spring-managed Session: taking the current
|
||||
* thread-bound Session if available (e.g. in an Open-Session-in-View scenario),
|
||||
* creating a new Session for the current transaction otherwise.
|
||||
* <p>Switch this flag to "true" in order to enforce use of a Hibernate-managed Session.
|
||||
* Note that this requires {@link org.hibernate.SessionFactory#getCurrentSession()}
|
||||
* to always return a proper Session when called for a Spring-managed transaction;
|
||||
* transaction begin will fail if the <code>getCurrentSession()</code> call fails.
|
||||
* <p>This mode will typically be used in combination with a custom Hibernate
|
||||
* {@link org.hibernate.context.CurrentSessionContext} implementation that stores
|
||||
* Sessions in a place other than Spring's TransactionSynchronizationManager.
|
||||
* It may also be used in combination with Spring's Open-Session-in-View support
|
||||
* (using Spring's default {@link SpringSessionContext}), in which case it subtly
|
||||
* differs from the Spring-managed Session mode: The pre-bound Session will <i>not</i>
|
||||
* receive a <code>clear()</code> call (on rollback) or a <code>disconnect()</code>
|
||||
* call (on transaction completion) in such a scenario; this is rather left up
|
||||
* to a custom CurrentSessionContext implementation (if desired).
|
||||
*/
|
||||
public void setHibernateManagedSession(boolean hibernateManagedSession) {
|
||||
this.hibernateManagedSession = hibernateManagedSession;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
if (getSessionFactory() == null) {
|
||||
throw new IllegalArgumentException("Property 'sessionFactory' is required");
|
||||
}
|
||||
|
||||
// Check for SessionFactory's DataSource.
|
||||
if (this.autodetectDataSource && getDataSource() == null) {
|
||||
DataSource sfds = SessionFactoryUtils.getDataSource(getSessionFactory());
|
||||
if (sfds != null) {
|
||||
// Use the SessionFactory's DataSource for exposing transactions to JDBC code.
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Using DataSource [" + sfds +
|
||||
"] of Hibernate SessionFactory for HibernateTransactionManager");
|
||||
}
|
||||
setDataSource(sfds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Object getResourceFactory() {
|
||||
return getSessionFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doGetTransaction() {
|
||||
HibernateTransactionObject txObject = new HibernateTransactionObject();
|
||||
txObject.setSavepointAllowed(isNestedTransactionAllowed());
|
||||
|
||||
SessionHolder sessionHolder =
|
||||
(SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());
|
||||
if (sessionHolder != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Found thread-bound Session [" + sessionHolder.getSession() + "] for Hibernate transaction");
|
||||
}
|
||||
txObject.setSessionHolder(sessionHolder);
|
||||
}
|
||||
else if (this.hibernateManagedSession) {
|
||||
try {
|
||||
Session session = (Session) ReflectionUtils.invokeMethod(getCurrentSessionMethod, this.sessionFactory);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Found Hibernate-managed Session [" + session + "] for Spring-managed transaction");
|
||||
}
|
||||
txObject.setExistingSession(session);
|
||||
}
|
||||
catch (HibernateException ex) {
|
||||
throw new DataAccessResourceFailureException(
|
||||
"Could not obtain Hibernate-managed Session for Spring-managed transaction", ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (getDataSource() != null) {
|
||||
ConnectionHolder conHolder = (ConnectionHolder)
|
||||
TransactionSynchronizationManager.getResource(getDataSource());
|
||||
txObject.setConnectionHolder(conHolder);
|
||||
}
|
||||
|
||||
return txObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isExistingTransaction(Object transaction) {
|
||||
HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
|
||||
return (txObject.hasSpringManagedTransaction() ||
|
||||
(this.hibernateManagedSession && txObject.hasHibernateManagedTransaction()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBegin(Object transaction, TransactionDefinition definition) {
|
||||
HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
|
||||
|
||||
if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
|
||||
throw new IllegalTransactionStateException(
|
||||
"Pre-bound JDBC Connection found! HibernateTransactionManager does not support " +
|
||||
"running within DataSourceTransactionManager if told to manage the DataSource itself. " +
|
||||
"It is recommended to use a single HibernateTransactionManager for all transactions " +
|
||||
"on a single DataSource, no matter whether Hibernate or JDBC access.");
|
||||
}
|
||||
|
||||
Session session = null;
|
||||
|
||||
try {
|
||||
if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) {
|
||||
Session newSession = SessionFactoryUtils.openSession(getSessionFactory());
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Opened new Session [" + newSession + "] for Hibernate transaction");
|
||||
}
|
||||
txObject.setSession(newSession);
|
||||
}
|
||||
|
||||
session = txObject.getSessionHolder().getSession();
|
||||
|
||||
if (this.prepareConnection && isSameConnectionForEntireSession(session)) {
|
||||
// We're allowed to change the transaction settings of the JDBC Connection.
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Preparing JDBC Connection of Hibernate Session [" + session + "]");
|
||||
}
|
||||
Connection con = ((SessionImplementor) session).connection();
|
||||
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
|
||||
txObject.setPreviousIsolationLevel(previousIsolationLevel);
|
||||
}
|
||||
else {
|
||||
// Not allowed to change the transaction settings of the JDBC Connection.
|
||||
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
|
||||
// We should set a specific isolation level but are not allowed to...
|
||||
throw new InvalidIsolationLevelException(
|
||||
"HibernateTransactionManager is not allowed to support custom isolation levels: " +
|
||||
"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).");
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Not preparing JDBC Connection of Hibernate Session [" + session + "]");
|
||||
}
|
||||
}
|
||||
|
||||
if (definition.isReadOnly() && txObject.isNewSession()) {
|
||||
// Just set to NEVER in case of a new Session for this transaction.
|
||||
session.setFlushMode(FlushMode.MANUAL);
|
||||
}
|
||||
|
||||
if (!definition.isReadOnly() && !txObject.isNewSession()) {
|
||||
// We need AUTO or COMMIT for a non-read-only transaction.
|
||||
FlushMode flushMode = session.getFlushMode();
|
||||
if (FlushMode.isManualFlushMode(session.getFlushMode())) {
|
||||
session.setFlushMode(FlushMode.AUTO);
|
||||
txObject.getSessionHolder().setPreviousFlushMode(flushMode);
|
||||
}
|
||||
}
|
||||
|
||||
Transaction hibTx;
|
||||
|
||||
// Register transaction timeout.
|
||||
int timeout = determineTimeout(definition);
|
||||
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
|
||||
// Use Hibernate's own transaction timeout mechanism on Hibernate 3.1+
|
||||
// Applies to all statements, also to inserts, updates and deletes!
|
||||
hibTx = session.getTransaction();
|
||||
hibTx.setTimeout(timeout);
|
||||
hibTx.begin();
|
||||
}
|
||||
else {
|
||||
// Open a plain Hibernate transaction without specified timeout.
|
||||
hibTx = session.beginTransaction();
|
||||
}
|
||||
|
||||
// Add the Hibernate transaction to the session holder.
|
||||
txObject.getSessionHolder().setTransaction(hibTx);
|
||||
|
||||
// Register the Hibernate Session's JDBC Connection for the DataSource, if set.
|
||||
if (getDataSource() != null) {
|
||||
Connection con = ((SessionImplementor) session).connection();
|
||||
ConnectionHolder conHolder = new ConnectionHolder(con);
|
||||
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
|
||||
conHolder.setTimeoutInSeconds(timeout);
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Exposing Hibernate transaction as JDBC transaction [" + con + "]");
|
||||
}
|
||||
TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
|
||||
txObject.setConnectionHolder(conHolder);
|
||||
}
|
||||
|
||||
// Bind the session holder to the thread.
|
||||
if (txObject.isNewSessionHolder()) {
|
||||
TransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder());
|
||||
}
|
||||
txObject.getSessionHolder().setSynchronizedWithTransaction(true);
|
||||
}
|
||||
|
||||
catch (Exception ex) {
|
||||
if (txObject.isNewSession()) {
|
||||
try {
|
||||
if (session.getTransaction().isActive()) {
|
||||
session.getTransaction().rollback();
|
||||
}
|
||||
}
|
||||
catch (Throwable ex2) {
|
||||
logger.debug("Could not rollback Session after failed transaction begin", ex);
|
||||
}
|
||||
finally {
|
||||
SessionFactoryUtils.closeSession(session);
|
||||
}
|
||||
}
|
||||
throw new CannotCreateTransactionException("Could not open Hibernate Session for transaction", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doSuspend(Object transaction) {
|
||||
HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
|
||||
txObject.setSessionHolder(null);
|
||||
SessionHolder sessionHolder =
|
||||
(SessionHolder) TransactionSynchronizationManager.unbindResource(getSessionFactory());
|
||||
txObject.setConnectionHolder(null);
|
||||
ConnectionHolder connectionHolder = null;
|
||||
if (getDataSource() != null) {
|
||||
connectionHolder = (ConnectionHolder) TransactionSynchronizationManager.unbindResource(getDataSource());
|
||||
}
|
||||
return new SuspendedResourcesHolder(sessionHolder, connectionHolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doResume(Object transaction, Object suspendedResources) {
|
||||
SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder) suspendedResources;
|
||||
if (TransactionSynchronizationManager.hasResource(getSessionFactory())) {
|
||||
// From non-transactional code running in active transaction synchronization
|
||||
// -> can be safely removed, will be closed on transaction completion.
|
||||
TransactionSynchronizationManager.unbindResource(getSessionFactory());
|
||||
}
|
||||
TransactionSynchronizationManager.bindResource(getSessionFactory(), resourcesHolder.getSessionHolder());
|
||||
if (getDataSource() != null) {
|
||||
TransactionSynchronizationManager.bindResource(getDataSource(), resourcesHolder.getConnectionHolder());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doCommit(DefaultTransactionStatus status) {
|
||||
HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();
|
||||
if (status.isDebug()) {
|
||||
logger.debug("Committing Hibernate transaction on Session [" +
|
||||
txObject.getSessionHolder().getSession() + "]");
|
||||
}
|
||||
try {
|
||||
txObject.getSessionHolder().getTransaction().commit();
|
||||
}
|
||||
catch (org.hibernate.TransactionException ex) {
|
||||
// assumably from commit call to the underlying JDBC connection
|
||||
throw new TransactionSystemException("Could not commit Hibernate transaction", ex);
|
||||
}
|
||||
catch (HibernateException ex) {
|
||||
// assumably failed to flush changes to database
|
||||
throw convertHibernateAccessException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRollback(DefaultTransactionStatus status) {
|
||||
HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();
|
||||
if (status.isDebug()) {
|
||||
logger.debug("Rolling back Hibernate transaction on Session [" +
|
||||
txObject.getSessionHolder().getSession() + "]");
|
||||
}
|
||||
try {
|
||||
txObject.getSessionHolder().getTransaction().rollback();
|
||||
}
|
||||
catch (org.hibernate.TransactionException ex) {
|
||||
throw new TransactionSystemException("Could not roll back Hibernate transaction", ex);
|
||||
}
|
||||
catch (HibernateException ex) {
|
||||
// Shouldn't really happen, as a rollback doesn't cause a flush.
|
||||
throw convertHibernateAccessException(ex);
|
||||
}
|
||||
finally {
|
||||
if (!txObject.isNewSession() && !this.hibernateManagedSession) {
|
||||
// Clear all pending inserts/updates/deletes in the Session.
|
||||
// Necessary for pre-bound Sessions, to avoid inconsistent state.
|
||||
txObject.getSessionHolder().getSession().clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSetRollbackOnly(DefaultTransactionStatus status) {
|
||||
HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();
|
||||
if (status.isDebug()) {
|
||||
logger.debug("Setting Hibernate transaction on Session [" +
|
||||
txObject.getSessionHolder().getSession() + "] rollback-only");
|
||||
}
|
||||
txObject.setRollbackOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doCleanupAfterCompletion(Object transaction) {
|
||||
HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
|
||||
|
||||
// Remove the session holder from the thread.
|
||||
if (txObject.isNewSessionHolder()) {
|
||||
TransactionSynchronizationManager.unbindResource(getSessionFactory());
|
||||
}
|
||||
|
||||
// Remove the JDBC connection holder from the thread, if exposed.
|
||||
if (getDataSource() != null) {
|
||||
TransactionSynchronizationManager.unbindResource(getDataSource());
|
||||
}
|
||||
|
||||
Session session = txObject.getSessionHolder().getSession();
|
||||
if (this.prepareConnection && session.isConnected() && isSameConnectionForEntireSession(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.
|
||||
try {
|
||||
Connection con = ((SessionImplementor) session).connection();
|
||||
DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
|
||||
}
|
||||
catch (HibernateException ex) {
|
||||
logger.debug("Could not access JDBC Connection of Hibernate Session", ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (txObject.isNewSession()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Closing Hibernate Session [" + session + "] after transaction");
|
||||
}
|
||||
SessionFactoryUtils.closeSession(session);
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Not closing pre-bound Hibernate Session [" + session + "] after transaction");
|
||||
}
|
||||
if (txObject.getSessionHolder().getPreviousFlushMode() != null) {
|
||||
session.setFlushMode(txObject.getSessionHolder().getPreviousFlushMode());
|
||||
}
|
||||
if (!this.hibernateManagedSession) {
|
||||
session.disconnect();
|
||||
}
|
||||
}
|
||||
txObject.getSessionHolder().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the given Hibernate Session will always hold the same
|
||||
* JDBC Connection. This is used to check whether the transaction manager
|
||||
* can safely prepare and clean up the JDBC Connection used for a transaction.
|
||||
* <p>The default implementation checks the Session's connection release mode
|
||||
* to be "on_close".
|
||||
* @param session the Hibernate Session to check
|
||||
* @see org.hibernate.engine.transaction.spi.TransactionContext#getConnectionReleaseMode()
|
||||
* @see org.hibernate.ConnectionReleaseMode#ON_CLOSE
|
||||
*/
|
||||
protected boolean isSameConnectionForEntireSession(Session session) {
|
||||
if (!(session instanceof TransactionContext)) {
|
||||
// The best we can do is to assume we're safe.
|
||||
return true;
|
||||
}
|
||||
ConnectionReleaseMode releaseMode = ((TransactionContext) session).getConnectionReleaseMode();
|
||||
return ConnectionReleaseMode.ON_CLOSE.equals(releaseMode);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert the given HibernateException to an appropriate exception
|
||||
* from the <code>org.springframework.dao</code> hierarchy.
|
||||
* <p>Will automatically apply a specified SQLExceptionTranslator to a
|
||||
* Hibernate JDBCException, else rely on Hibernate's default translation.
|
||||
* @param ex HibernateException that occured
|
||||
* @return a corresponding DataAccessException
|
||||
* @see SessionFactoryUtils#convertHibernateAccessException
|
||||
*/
|
||||
protected DataAccessException convertHibernateAccessException(HibernateException ex) {
|
||||
return SessionFactoryUtils.convertHibernateAccessException(ex);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hibernate transaction object, representing a SessionHolder.
|
||||
* Used as transaction object by HibernateTransactionManager.
|
||||
*/
|
||||
private class HibernateTransactionObject extends JdbcTransactionObjectSupport {
|
||||
|
||||
private SessionHolder sessionHolder;
|
||||
|
||||
private boolean newSessionHolder;
|
||||
|
||||
private boolean newSession;
|
||||
|
||||
public void setSession(Session session) {
|
||||
this.sessionHolder = new SessionHolder(session);
|
||||
this.newSessionHolder = true;
|
||||
this.newSession = true;
|
||||
}
|
||||
|
||||
public void setExistingSession(Session session) {
|
||||
this.sessionHolder = new SessionHolder(session);
|
||||
this.newSessionHolder = true;
|
||||
this.newSession = false;
|
||||
}
|
||||
|
||||
public void setSessionHolder(SessionHolder sessionHolder) {
|
||||
this.sessionHolder = sessionHolder;
|
||||
this.newSessionHolder = false;
|
||||
this.newSession = false;
|
||||
}
|
||||
|
||||
public SessionHolder getSessionHolder() {
|
||||
return this.sessionHolder;
|
||||
}
|
||||
|
||||
public boolean isNewSessionHolder() {
|
||||
return this.newSessionHolder;
|
||||
}
|
||||
|
||||
public boolean isNewSession() {
|
||||
return this.newSession;
|
||||
}
|
||||
|
||||
public boolean hasSpringManagedTransaction() {
|
||||
return (this.sessionHolder != null && this.sessionHolder.getTransaction() != null);
|
||||
}
|
||||
|
||||
public boolean hasHibernateManagedTransaction() {
|
||||
return (this.sessionHolder != null && this.sessionHolder.getSession().getTransaction().isActive());
|
||||
}
|
||||
|
||||
public void setRollbackOnly() {
|
||||
this.sessionHolder.setRollbackOnly();
|
||||
if (hasConnectionHolder()) {
|
||||
getConnectionHolder().setRollbackOnly();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRollbackOnly() {
|
||||
return this.sessionHolder.isRollbackOnly() ||
|
||||
(hasConnectionHolder() && getConnectionHolder().isRollbackOnly());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
try {
|
||||
this.sessionHolder.getSession().flush();
|
||||
}
|
||||
catch (HibernateException ex) {
|
||||
throw convertHibernateAccessException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Holder for suspended resources.
|
||||
* Used internally by <code>doSuspend</code> and <code>doResume</code>.
|
||||
*/
|
||||
private static class SuspendedResourcesHolder {
|
||||
|
||||
private final SessionHolder sessionHolder;
|
||||
|
||||
private final ConnectionHolder connectionHolder;
|
||||
|
||||
private SuspendedResourcesHolder(SessionHolder sessionHolder, ConnectionHolder conHolder) {
|
||||
this.sessionHolder = sessionHolder;
|
||||
this.connectionHolder = conHolder;
|
||||
}
|
||||
|
||||
private SessionHolder getSessionHolder() {
|
||||
return this.sessionHolder;
|
||||
}
|
||||
|
||||
private ConnectionHolder getConnectionHolder() {
|
||||
return this.connectionHolder;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,405 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.orm.hibernate4;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.hibernate.Interceptor;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.cfg.NamingStrategy;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
import org.springframework.core.io.support.ResourcePatternUtils;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.FactoryBean} that creates a Hibernate
|
||||
* {@link org.hibernate.SessionFactory}. This is the usual way to set up a shared
|
||||
* Hibernate SessionFactory in a Spring application context; the SessionFactory can
|
||||
* then be passed to Hibernate-based data access objects via dependency injection.
|
||||
*
|
||||
* <p><b>NOTE:</b> This variant of LocalSessionFactoryBean requires Hibernate 4.0 or higher.
|
||||
* It is similar in role to the same-named class in the <code>orm.hibernate3</code> package.
|
||||
* However, in practice, it is closer to <code>AnnotationSessionFactoryBean</code> since
|
||||
* its core purpose is to bootstrap a <code>SessionFactory</code> from annotation scanning.
|
||||
*
|
||||
* <p><b>NOTE:</b> To set up Hibernate 4 for Spring-driven JTA transactions, make
|
||||
* sure to either specify the {@link #setJtaTransactionManager "jtaTransactionManager"}
|
||||
* bean property or to set the "hibernate.transaction.factory_class" property to
|
||||
* {@link org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory}.
|
||||
* Otherwise, Hibernate's smart flushing mechanism won't work properly.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see #setDataSource
|
||||
* @see #setPackagesToScan
|
||||
*/
|
||||
public class LocalSessionFactoryBean extends HibernateExceptionTranslator
|
||||
implements FactoryBean<SessionFactory>, ResourceLoaderAware, InitializingBean, DisposableBean {
|
||||
|
||||
private DataSource dataSource;
|
||||
|
||||
private Resource[] configLocations;
|
||||
|
||||
private String[] mappingResources;
|
||||
|
||||
private Resource[] mappingLocations;
|
||||
|
||||
private Resource[] cacheableMappingLocations;
|
||||
|
||||
private Resource[] mappingJarLocations;
|
||||
|
||||
private Resource[] mappingDirectoryLocations;
|
||||
|
||||
private Interceptor entityInterceptor;
|
||||
|
||||
private NamingStrategy namingStrategy;
|
||||
|
||||
private Properties hibernateProperties;
|
||||
|
||||
private Class<?>[] annotatedClasses;
|
||||
|
||||
private String[] annotatedPackages;
|
||||
|
||||
private String[] packagesToScan;
|
||||
|
||||
private Object jtaTransactionManager;
|
||||
|
||||
private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
|
||||
|
||||
private Configuration configuration;
|
||||
|
||||
private SessionFactory sessionFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Set the DataSource to be used by the SessionFactory.
|
||||
* If set, this will override corresponding settings in Hibernate properties.
|
||||
* <p>If this is set, the Hibernate settings should not define
|
||||
* a connection provider to avoid meaningless double configuration.
|
||||
*/
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the location of a single Hibernate XML config file, for example as
|
||||
* classpath resource "classpath:hibernate.cfg.xml".
|
||||
* <p>Note: Can be omitted when all necessary properties and mapping
|
||||
* resources are specified locally via this bean.
|
||||
* @see org.hibernate.cfg.Configuration#configure(java.net.URL)
|
||||
*/
|
||||
public void setConfigLocation(Resource configLocation) {
|
||||
this.configLocations = new Resource[] {configLocation};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the locations of multiple Hibernate XML config files, for example as
|
||||
* classpath resources "classpath:hibernate.cfg.xml,classpath:extension.cfg.xml".
|
||||
* <p>Note: Can be omitted when all necessary properties and mapping
|
||||
* resources are specified locally via this bean.
|
||||
* @see org.hibernate.cfg.Configuration#configure(java.net.URL)
|
||||
*/
|
||||
public void setConfigLocations(Resource[] configLocations) {
|
||||
this.configLocations = configLocations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Hibernate mapping resources to be found in the class path,
|
||||
* like "example.hbm.xml" or "mypackage/example.hbm.xml".
|
||||
* Analogous to mapping entries in a Hibernate XML config file.
|
||||
* Alternative to the more generic setMappingLocations method.
|
||||
* <p>Can be used to add to mappings from a Hibernate XML config file,
|
||||
* or to specify all mappings locally.
|
||||
* @see #setMappingLocations
|
||||
* @see org.hibernate.cfg.Configuration#addResource
|
||||
*/
|
||||
public void setMappingResources(String[] mappingResources) {
|
||||
this.mappingResources = mappingResources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set locations of Hibernate mapping files, for example as classpath
|
||||
* resource "classpath:example.hbm.xml". Supports any resource location
|
||||
* via Spring's resource abstraction, for example relative paths like
|
||||
* "WEB-INF/mappings/example.hbm.xml" when running in an application context.
|
||||
* <p>Can be used to add to mappings from a Hibernate XML config file,
|
||||
* or to specify all mappings locally.
|
||||
* @see org.hibernate.cfg.Configuration#addInputStream
|
||||
*/
|
||||
public void setMappingLocations(Resource[] mappingLocations) {
|
||||
this.mappingLocations = mappingLocations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set locations of cacheable Hibernate mapping files, for example as web app
|
||||
* resource "/WEB-INF/mapping/example.hbm.xml". Supports any resource location
|
||||
* via Spring's resource abstraction, as long as the resource can be resolved
|
||||
* in the file system.
|
||||
* <p>Can be used to add to mappings from a Hibernate XML config file,
|
||||
* or to specify all mappings locally.
|
||||
* @see org.hibernate.cfg.Configuration#addCacheableFile(java.io.File)
|
||||
*/
|
||||
public void setCacheableMappingLocations(Resource[] cacheableMappingLocations) {
|
||||
this.cacheableMappingLocations = cacheableMappingLocations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set locations of jar files that contain Hibernate mapping resources,
|
||||
* like "WEB-INF/lib/example.hbm.jar".
|
||||
* <p>Can be used to add to mappings from a Hibernate XML config file,
|
||||
* or to specify all mappings locally.
|
||||
* @see org.hibernate.cfg.Configuration#addJar(java.io.File)
|
||||
*/
|
||||
public void setMappingJarLocations(Resource[] mappingJarLocations) {
|
||||
this.mappingJarLocations = mappingJarLocations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set locations of directories that contain Hibernate mapping resources,
|
||||
* like "WEB-INF/mappings".
|
||||
* <p>Can be used to add to mappings from a Hibernate XML config file,
|
||||
* or to specify all mappings locally.
|
||||
* @see org.hibernate.cfg.Configuration#addDirectory(java.io.File)
|
||||
*/
|
||||
public void setMappingDirectoryLocations(Resource[] mappingDirectoryLocations) {
|
||||
this.mappingDirectoryLocations = mappingDirectoryLocations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a Hibernate entity interceptor that allows to inspect and change
|
||||
* property values before writing to and reading from the database.
|
||||
* Will get applied to any new Session created by this factory.
|
||||
* @see org.hibernate.cfg.Configuration#setInterceptor
|
||||
*/
|
||||
public void setEntityInterceptor(Interceptor entityInterceptor) {
|
||||
this.entityInterceptor = entityInterceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a Hibernate NamingStrategy for the SessionFactory, determining the
|
||||
* physical column and table names given the info in the mapping document.
|
||||
* @see org.hibernate.cfg.Configuration#setNamingStrategy
|
||||
*/
|
||||
public void setNamingStrategy(NamingStrategy namingStrategy) {
|
||||
this.namingStrategy = namingStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Hibernate properties, such as "hibernate.dialect".
|
||||
* <p>Note: Do not specify a transaction provider here when using
|
||||
* Spring-driven transactions. It is also advisable to omit connection
|
||||
* provider settings and use a Spring-set DataSource instead.
|
||||
* @see #setDataSource
|
||||
*/
|
||||
public void setHibernateProperties(Properties hibernateProperties) {
|
||||
this.hibernateProperties = hibernateProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Hibernate properties, if any. Mainly available for
|
||||
* configuration through property paths that specify individual keys.
|
||||
*/
|
||||
public Properties getHibernateProperties() {
|
||||
if (this.hibernateProperties == null) {
|
||||
this.hibernateProperties = new Properties();
|
||||
}
|
||||
return this.hibernateProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify annotated entity classes to register with this Hibernate SessionFactory.
|
||||
* @see org.hibernate.cfg.Configuration#addAnnotatedClass(Class)
|
||||
*/
|
||||
public void setAnnotatedClasses(Class<?>[] annotatedClasses) {
|
||||
this.annotatedClasses = annotatedClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the names of annotated packages, for which package-level
|
||||
* annotation metadata will be read.
|
||||
* @see org.hibernate.cfg.Configuration#addPackage(String)
|
||||
*/
|
||||
public void setAnnotatedPackages(String[] annotatedPackages) {
|
||||
this.annotatedPackages = annotatedPackages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify packages to search for autodetection of your entity classes in the
|
||||
* classpath. This is analogous to Spring's component-scan feature
|
||||
* ({@link org.springframework.context.annotation.ClassPathBeanDefinitionScanner}).
|
||||
*/
|
||||
public void setPackagesToScan(String... packagesToScan) {
|
||||
this.packagesToScan = packagesToScan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Spring {@link org.springframework.transaction.jta.JtaTransactionManager}
|
||||
* or the JTA {@link javax.transaction.TransactionManager} to be used with Hibernate,
|
||||
* if any.
|
||||
* @see LocalSessionFactoryBuilder#setJtaTransactionManager
|
||||
*/
|
||||
public void setJtaTransactionManager(Object jtaTransactionManager) {
|
||||
this.jtaTransactionManager = jtaTransactionManager;
|
||||
}
|
||||
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws IOException {
|
||||
LocalSessionFactoryBuilder sfb = new LocalSessionFactoryBuilder(this.dataSource, this.resourcePatternResolver);
|
||||
|
||||
if (this.configLocations != null) {
|
||||
for (Resource resource : this.configLocations) {
|
||||
// Load Hibernate configuration from given location.
|
||||
sfb.configure(resource.getURL());
|
||||
}
|
||||
}
|
||||
|
||||
if (this.mappingResources != null) {
|
||||
// Register given Hibernate mapping definitions, contained in resource files.
|
||||
for (String mapping : this.mappingResources) {
|
||||
Resource mr = new ClassPathResource(mapping.trim(), this.resourcePatternResolver.getClassLoader());
|
||||
sfb.addInputStream(mr.getInputStream());
|
||||
}
|
||||
}
|
||||
|
||||
if (this.mappingLocations != null) {
|
||||
// Register given Hibernate mapping definitions, contained in resource files.
|
||||
for (Resource resource : this.mappingLocations) {
|
||||
sfb.addInputStream(resource.getInputStream());
|
||||
}
|
||||
}
|
||||
|
||||
if (this.cacheableMappingLocations != null) {
|
||||
// Register given cacheable Hibernate mapping definitions, read from the file system.
|
||||
for (Resource resource : this.cacheableMappingLocations) {
|
||||
sfb.addCacheableFile(resource.getFile());
|
||||
}
|
||||
}
|
||||
|
||||
if (this.mappingJarLocations != null) {
|
||||
// Register given Hibernate mapping definitions, contained in jar files.
|
||||
for (Resource resource : this.mappingJarLocations) {
|
||||
sfb.addJar(resource.getFile());
|
||||
}
|
||||
}
|
||||
|
||||
if (this.mappingDirectoryLocations != null) {
|
||||
// Register all Hibernate mapping definitions in the given directories.
|
||||
for (Resource resource : this.mappingDirectoryLocations) {
|
||||
File file = resource.getFile();
|
||||
if (!file.isDirectory()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Mapping directory location [" + resource + "] does not denote a directory");
|
||||
}
|
||||
sfb.addDirectory(file);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.entityInterceptor != null) {
|
||||
sfb.setInterceptor(this.entityInterceptor);
|
||||
}
|
||||
|
||||
if (this.namingStrategy != null) {
|
||||
sfb.setNamingStrategy(this.namingStrategy);
|
||||
}
|
||||
|
||||
if (this.hibernateProperties != null) {
|
||||
sfb.addProperties(this.hibernateProperties);
|
||||
}
|
||||
|
||||
if (this.annotatedClasses != null) {
|
||||
sfb.addAnnotatedClasses(this.annotatedClasses);
|
||||
}
|
||||
|
||||
if (this.annotatedPackages != null) {
|
||||
sfb.addPackages(this.annotatedPackages);
|
||||
}
|
||||
|
||||
if (this.packagesToScan != null) {
|
||||
sfb.scanPackages(this.packagesToScan);
|
||||
}
|
||||
|
||||
if (this.jtaTransactionManager != null) {
|
||||
sfb.setJtaTransactionManager(this.jtaTransactionManager);
|
||||
}
|
||||
|
||||
// Build SessionFactory instance.
|
||||
this.configuration = sfb;
|
||||
this.sessionFactory = buildSessionFactory(sfb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can override this method to perform custom initialization
|
||||
* of the SessionFactory instance, creating it via the given Configuration
|
||||
* object that got prepared by this LocalSessionFactoryBean.
|
||||
* <p>The default implementation invokes LocalSessionFactoryBuilder's buildSessionFactory.
|
||||
* A custom implementation could prepare the instance in a specific way (e.g. applying
|
||||
* a custom ServiceRegistry) or use a custom SessionFactoryImpl subclass.
|
||||
* @param sfb LocalSessionFactoryBuilder prepared by this LocalSessionFactoryBean
|
||||
* @return the SessionFactory instance
|
||||
* @see LocalSessionFactoryBuilder#buildSessionFactory
|
||||
*/
|
||||
protected SessionFactory buildSessionFactory(LocalSessionFactoryBuilder sfb) {
|
||||
return sfb.buildSessionFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Hibernate Configuration object used to build the SessionFactory.
|
||||
* Allows for access to configuration metadata stored there (rarely needed).
|
||||
* @throws IllegalStateException if the Configuration object has not been initialized yet
|
||||
*/
|
||||
public final Configuration getConfiguration() {
|
||||
if (this.configuration == null) {
|
||||
throw new IllegalStateException("Configuration not initialized yet");
|
||||
}
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
|
||||
public SessionFactory getObject() {
|
||||
return this.sessionFactory;
|
||||
}
|
||||
|
||||
public Class<?> getObjectType() {
|
||||
return (this.sessionFactory != null ? this.sessionFactory.getClass() : SessionFactory.class);
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public void destroy() {
|
||||
this.sessionFactory.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,260 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.orm.hibernate4;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.sql.DataSource;
|
||||
import javax.transaction.TransactionManager;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory;
|
||||
import org.hibernate.service.jta.platform.internal.WebSphereExtendedJtaPlatform;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
import org.springframework.core.io.support.ResourcePatternUtils;
|
||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
import org.springframework.transaction.jta.JtaTransactionManager;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* A Spring-provided extension of the standard Hibernate {@link Configuration} class,
|
||||
* adding {@link SpringSessionContext} as a default and providing convenient ways
|
||||
* to specify a DataSource and an application class loader.
|
||||
*
|
||||
* <p>This is designed for programmatic use, e.g. in {@code @Bean} factory methods.
|
||||
* Consider using {@link LocalSessionFactoryBean} for XML bean definition files.
|
||||
*
|
||||
* <p><b>NOTE:</b> To set up Hibernate 4 for Spring-driven JTA transactions, make
|
||||
* sure to either use the {@link #setJtaTransactionManager} method or to set the
|
||||
* "hibernate.transaction.factory_class" property to {@link CMTTransactionFactory}.
|
||||
* Otherwise, Hibernate's smart flushing mechanism won't work properly.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see LocalSessionFactoryBean
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class LocalSessionFactoryBuilder extends Configuration {
|
||||
|
||||
private static final String RESOURCE_PATTERN = "/**/*.class";
|
||||
|
||||
private static final TypeFilter[] ENTITY_TYPE_FILTERS = new TypeFilter[] {
|
||||
new AnnotationTypeFilter(Entity.class, false),
|
||||
new AnnotationTypeFilter(Embeddable.class, false),
|
||||
new AnnotationTypeFilter(MappedSuperclass.class, false)};
|
||||
|
||||
private static final Method addAnnotatedClassMethod =
|
||||
ClassUtils.getMethod(Configuration.class, "addAnnotatedClass", Class.class);
|
||||
|
||||
private static final Method addPackageMethod =
|
||||
ClassUtils.getMethod(Configuration.class, "addPackage", String.class);
|
||||
|
||||
|
||||
private final ResourcePatternResolver resourcePatternResolver;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new LocalSessionFactoryBuilder for the given DataSource.
|
||||
* @param dataSource the JDBC DataSource that the resulting Hibernate SessionFactory should be using
|
||||
* (may be <code>null</code>)
|
||||
*/
|
||||
public LocalSessionFactoryBuilder(DataSource dataSource) {
|
||||
this(dataSource, new PathMatchingResourcePatternResolver());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new LocalSessionFactoryBuilder for the given DataSource.
|
||||
* @param dataSource the JDBC DataSource that the resulting Hibernate SessionFactory should be using
|
||||
* (may be <code>null</code>)
|
||||
* @param classLoader the ClassLoader to load application classes from
|
||||
*/
|
||||
public LocalSessionFactoryBuilder(DataSource dataSource, ClassLoader classLoader) {
|
||||
this(dataSource, new PathMatchingResourcePatternResolver(classLoader));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new LocalSessionFactoryBuilder for the given DataSource.
|
||||
* @param dataSource the JDBC DataSource that the resulting Hibernate SessionFactory should be using
|
||||
* (may be <code>null</code>)
|
||||
* @param resourceLoader the ResourceLoader to load application classes from
|
||||
*/
|
||||
public LocalSessionFactoryBuilder(DataSource dataSource, ResourceLoader resourceLoader) {
|
||||
getProperties().put(Environment.CURRENT_SESSION_CONTEXT_CLASS, SpringSessionContext.class.getName());
|
||||
if (dataSource != null) {
|
||||
getProperties().put(Environment.DATASOURCE, dataSource);
|
||||
}
|
||||
getProperties().put(AvailableSettings.APP_CLASSLOADER, resourceLoader.getClassLoader());
|
||||
this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the Spring {@link JtaTransactionManager} or the JTA {@link TransactionManager}
|
||||
* to be used with Hibernate, if any. Allows for using a Spring-managed transaction
|
||||
* manager for Hibernate 4's session and cache synchronization, with the
|
||||
* "hibernate.transaction.jta.platform" automatically set to it. Also sets
|
||||
* "hibernate.transaction.factory_class" to {@link CMTTransactionFactory},
|
||||
* instructing Hibernate to interact with externally managed transactions.
|
||||
* <p>A passed-in Spring {@link JtaTransactionManager} needs to contain a JTA
|
||||
* {@link TransactionManager} reference to be usable here, except for the WebSphere
|
||||
* case where we'll automatically set {@link WebSphereExtendedJtaPlatform} accordingly.
|
||||
* <p>Note: If this is set, the Hibernate settings should not contain a JTA platform
|
||||
* setting to avoid meaningless double 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) {
|
||||
getProperties().put(AvailableSettings.JTA_PLATFORM, new WebSphereExtendedJtaPlatform());
|
||||
}
|
||||
else {
|
||||
JtaTransactionManager jtaTm = (JtaTransactionManager) jtaTransactionManager;
|
||||
if (jtaTm.getTransactionManager() == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Can only apply JtaTransactionManager which has a TransactionManager reference set");
|
||||
}
|
||||
getProperties().put(AvailableSettings.JTA_PLATFORM,
|
||||
new ConfigurableJtaPlatform(jtaTm.getTransactionManager(), jtaTm.getUserTransaction()));
|
||||
}
|
||||
}
|
||||
else if (jtaTransactionManager instanceof TransactionManager) {
|
||||
getProperties().put(AvailableSettings.JTA_PLATFORM,
|
||||
new ConfigurableJtaPlatform((TransactionManager) jtaTransactionManager, null));
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(
|
||||
"Unknown transaction manager type: " + jtaTransactionManager.getClass().getName());
|
||||
}
|
||||
getProperties().put(AvailableSettings.TRANSACTION_STRATEGY, new CMTTransactionFactory());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given annotated classes in a batch.
|
||||
* @see #addAnnotatedClass
|
||||
* @see #scanPackages
|
||||
*/
|
||||
public LocalSessionFactoryBuilder addAnnotatedClasses(Class<?>... annotatedClasses) {
|
||||
for (Class<?> annotatedClass : annotatedClasses) {
|
||||
ReflectionUtils.invokeMethod(addAnnotatedClassMethod, this, annotatedClass);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given annotated packages in a batch.
|
||||
* @see #addPackage
|
||||
* @see #scanPackages
|
||||
*/
|
||||
public LocalSessionFactoryBuilder addPackages(String... annotatedPackages) {
|
||||
for (String annotatedPackage :annotatedPackages) {
|
||||
ReflectionUtils.invokeMethod(addPackageMethod, this, annotatedPackage);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform Spring-based scanning for entity classes, registering them
|
||||
* as annotated classes with this {@code Configuration}.
|
||||
* @param packagesToScan one or more Java package names
|
||||
* @throws HibernateException if scanning fails for any reason
|
||||
*/
|
||||
public LocalSessionFactoryBuilder scanPackages(String... packagesToScan) throws HibernateException {
|
||||
try {
|
||||
for (String pkg : packagesToScan) {
|
||||
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
|
||||
ClassUtils.convertClassNameToResourcePath(pkg) + RESOURCE_PATTERN;
|
||||
Resource[] resources = this.resourcePatternResolver.getResources(pattern);
|
||||
MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
|
||||
for (Resource resource : resources) {
|
||||
if (resource.isReadable()) {
|
||||
MetadataReader reader = readerFactory.getMetadataReader(resource);
|
||||
String className = reader.getClassMetadata().getClassName();
|
||||
if (matchesFilter(reader, readerFactory)) {
|
||||
addAnnotatedClasses(this.resourcePatternResolver.getClassLoader().loadClass(className));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new MappingException("Failed to scan classpath for unlisted classes", ex);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new MappingException("Failed to load annotated classes from classpath", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether any of the configured entity type filters matches
|
||||
* the current class descriptor contained in the metadata reader.
|
||||
*/
|
||||
private boolean matchesFilter(MetadataReader reader, MetadataReaderFactory readerFactory) throws IOException {
|
||||
for (TypeFilter filter : ENTITY_TYPE_FILTERS) {
|
||||
if (filter.match(reader, readerFactory)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build the {@code SessionFactory}.
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public SessionFactory buildSessionFactory() throws HibernateException {
|
||||
ClassLoader appClassLoader = (ClassLoader) getProperties().get(AvailableSettings.APP_CLASSLOADER);
|
||||
Thread currentThread = Thread.currentThread();
|
||||
ClassLoader threadContextClassLoader = currentThread.getContextClassLoader();
|
||||
boolean overrideClassLoader =
|
||||
(appClassLoader != null && !appClassLoader.equals(threadContextClassLoader));
|
||||
if (overrideClassLoader) {
|
||||
currentThread.setContextClassLoader(appClassLoader);
|
||||
}
|
||||
try {
|
||||
return super.buildSessionFactory();
|
||||
}
|
||||
finally {
|
||||
if (overrideClassLoader) {
|
||||
currentThread.setContextClassLoader(threadContextClassLoader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,209 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.orm.hibernate4;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.JDBCException;
|
||||
import org.hibernate.NonUniqueObjectException;
|
||||
import org.hibernate.NonUniqueResultException;
|
||||
import org.hibernate.ObjectDeletedException;
|
||||
import org.hibernate.PersistentObjectException;
|
||||
import org.hibernate.PropertyValueException;
|
||||
import org.hibernate.QueryException;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.StaleObjectStateException;
|
||||
import org.hibernate.StaleStateException;
|
||||
import org.hibernate.TransientObjectException;
|
||||
import org.hibernate.UnresolvableObjectException;
|
||||
import org.hibernate.WrongClassException;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.exception.ConstraintViolationException;
|
||||
import org.hibernate.exception.DataException;
|
||||
import org.hibernate.exception.JDBCConnectionException;
|
||||
import org.hibernate.exception.LockAcquisitionException;
|
||||
import org.hibernate.exception.SQLGrammarException;
|
||||
import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
|
||||
|
||||
import org.springframework.dao.CannotAcquireLockException;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
import org.springframework.jdbc.datasource.DataSourceUtils;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Helper class featuring methods for Hibernate Session handling.
|
||||
* Also provides support for exception translation.
|
||||
*
|
||||
* <p>Used internally by {@link HibernateTransactionManager}.
|
||||
* Can also be used directly in application code.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see HibernateExceptionTranslator
|
||||
* @see HibernateTransactionManager
|
||||
*/
|
||||
public abstract class SessionFactoryUtils {
|
||||
|
||||
/**
|
||||
* Order value for TransactionSynchronization objects that clean up Hibernate Sessions.
|
||||
* Returns <code>DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100</code>
|
||||
* to execute Session cleanup before JDBC Connection cleanup, if any.
|
||||
* @see org.springframework.jdbc.datasource.DataSourceUtils#CONNECTION_SYNCHRONIZATION_ORDER
|
||||
*/
|
||||
public static final int SESSION_SYNCHRONIZATION_ORDER =
|
||||
DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100;
|
||||
|
||||
/**
|
||||
* A Method handle for the <code>SessionFactory.openSession()</code> method.
|
||||
* The return value differs between Hibernate 3.x and 4.x; for cross-compilation purposes,
|
||||
* we have to use reflection here as long as we keep compiling against Hibernate 3.x jars.
|
||||
*/
|
||||
private static final Method openSessionMethod = ClassUtils.getMethod(SessionFactory.class, "openSession");
|
||||
|
||||
static final Log logger = LogFactory.getLog(SessionFactoryUtils.class);
|
||||
|
||||
|
||||
/**
|
||||
* Determine the DataSource of the given SessionFactory.
|
||||
* @param sessionFactory the SessionFactory to check
|
||||
* @return the DataSource, or <code>null</code> if none found
|
||||
* @see org.hibernate.engine.spi.SessionFactoryImplementor#getConnectionProvider
|
||||
*/
|
||||
public static DataSource getDataSource(SessionFactory sessionFactory) {
|
||||
if (sessionFactory instanceof SessionFactoryImplementor) {
|
||||
ConnectionProvider cp = ((SessionFactoryImplementor) sessionFactory).getConnectionProvider();
|
||||
return cp.unwrap(DataSource.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a new Session from the given SessionFactory.
|
||||
* <p>Bridges between Hibernate signature differences.
|
||||
* @param sessionFactory the SessionFactory to use
|
||||
* @return the new Session
|
||||
* @see org.hibernate.SessionFactory#openSession()
|
||||
*/
|
||||
public static Session openSession(SessionFactory sessionFactory) {
|
||||
return (Session) ReflectionUtils.invokeMethod(openSessionMethod, sessionFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform actual closing of the Hibernate Session,
|
||||
* catching and logging any cleanup exceptions thrown.
|
||||
* @param session the Hibernate Session to close (may be <code>null</code>)
|
||||
* @see org.hibernate.Session#close()
|
||||
*/
|
||||
public static void closeSession(Session session) {
|
||||
if (session != null) {
|
||||
try {
|
||||
session.close();
|
||||
}
|
||||
catch (HibernateException ex) {
|
||||
logger.debug("Could not close Hibernate Session", ex);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.debug("Unexpected exception on closing Hibernate Session", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given HibernateException to an appropriate exception
|
||||
* from the <code>org.springframework.dao</code> hierarchy.
|
||||
* @param ex HibernateException that occured
|
||||
* @return the corresponding DataAccessException instance
|
||||
* @see HibernateExceptionTranslator#convertHibernateAccessException
|
||||
* @see HibernateTransactionManager#convertHibernateAccessException
|
||||
*/
|
||||
public static DataAccessException convertHibernateAccessException(HibernateException ex) {
|
||||
if (ex instanceof JDBCConnectionException) {
|
||||
return new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof SQLGrammarException) {
|
||||
SQLGrammarException jdbcEx = (SQLGrammarException) ex;
|
||||
return new InvalidDataAccessResourceUsageException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
|
||||
}
|
||||
if (ex instanceof LockAcquisitionException) {
|
||||
LockAcquisitionException jdbcEx = (LockAcquisitionException) ex;
|
||||
return new CannotAcquireLockException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
|
||||
}
|
||||
if (ex instanceof ConstraintViolationException) {
|
||||
ConstraintViolationException jdbcEx = (ConstraintViolationException) ex;
|
||||
return new DataIntegrityViolationException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() +
|
||||
"]; constraint [" + jdbcEx.getConstraintName() + "]", ex);
|
||||
}
|
||||
if (ex instanceof DataException) {
|
||||
DataException jdbcEx = (DataException) ex;
|
||||
return new DataIntegrityViolationException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
|
||||
}
|
||||
if (ex instanceof JDBCException) {
|
||||
return new HibernateJdbcException((JDBCException) ex);
|
||||
}
|
||||
// end of JDBCException (subclass) handling
|
||||
|
||||
if (ex instanceof QueryException) {
|
||||
return new HibernateQueryException((QueryException) ex);
|
||||
}
|
||||
if (ex instanceof NonUniqueResultException) {
|
||||
return new IncorrectResultSizeDataAccessException(ex.getMessage(), 1, ex);
|
||||
}
|
||||
if (ex instanceof NonUniqueObjectException) {
|
||||
return new DuplicateKeyException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof PropertyValueException) {
|
||||
return new DataIntegrityViolationException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof PersistentObjectException) {
|
||||
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof TransientObjectException) {
|
||||
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof ObjectDeletedException) {
|
||||
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof UnresolvableObjectException) {
|
||||
return new HibernateObjectRetrievalFailureException((UnresolvableObjectException) ex);
|
||||
}
|
||||
if (ex instanceof WrongClassException) {
|
||||
return new HibernateObjectRetrievalFailureException((WrongClassException) ex);
|
||||
}
|
||||
if (ex instanceof StaleObjectStateException) {
|
||||
return new HibernateOptimisticLockingFailureException((StaleObjectStateException) ex);
|
||||
}
|
||||
if (ex instanceof StaleStateException) {
|
||||
return new HibernateOptimisticLockingFailureException((StaleStateException) ex);
|
||||
}
|
||||
|
||||
// fallback
|
||||
return new HibernateSystemException(ex);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.orm.hibernate4;
|
||||
|
||||
import org.hibernate.FlushMode;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.Transaction;
|
||||
|
||||
import org.springframework.transaction.support.ResourceHolderSupport;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Session holder, wrapping a Hibernate Session and a Hibernate Transaction.
|
||||
* HibernateTransactionManager binds instances of this class to the thread,
|
||||
* for a given SessionFactory.
|
||||
*
|
||||
* <p>Note: This is an SPI class, not intended to be used by applications.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see HibernateTransactionManager
|
||||
* @see SessionFactoryUtils
|
||||
*/
|
||||
public class SessionHolder extends ResourceHolderSupport {
|
||||
|
||||
private Session session;
|
||||
|
||||
private Transaction transaction;
|
||||
|
||||
private FlushMode previousFlushMode;
|
||||
|
||||
|
||||
public SessionHolder(Session session) {
|
||||
Assert.notNull(session, "Session must not be null");
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public Session getSession() {
|
||||
return this.session;
|
||||
}
|
||||
|
||||
public void setTransaction(Transaction transaction) {
|
||||
this.transaction = transaction;
|
||||
}
|
||||
|
||||
public Transaction getTransaction() {
|
||||
return this.transaction;
|
||||
}
|
||||
|
||||
public void setPreviousFlushMode(FlushMode previousFlushMode) {
|
||||
this.previousFlushMode = previousFlushMode;
|
||||
}
|
||||
|
||||
public FlushMode getPreviousFlushMode() {
|
||||
return this.previousFlushMode;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
super.clear();
|
||||
this.transaction = null;
|
||||
this.previousFlushMode = null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.orm.hibernate4;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.Session;
|
||||
|
||||
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
|
||||
|
||||
/**
|
||||
* Simple synchronization adapter that propagates a <code>flush()</code> call
|
||||
* to the underlying Hibernate Session. Used in combination with JTA.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
*/
|
||||
class SpringFlushSynchronization extends TransactionSynchronizationAdapter {
|
||||
|
||||
private final Session session;
|
||||
|
||||
|
||||
public SpringFlushSynchronization(Session session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
try {
|
||||
SessionFactoryUtils.logger.debug("Flushing Hibernate Session on explicit request");
|
||||
this.session.flush();
|
||||
}
|
||||
catch (HibernateException ex) {
|
||||
throw SessionFactoryUtils.convertHibernateAccessException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return (obj instanceof SpringFlushSynchronization &&
|
||||
this.session == ((SpringFlushSynchronization) obj).session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.session.hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.orm.hibernate4;
|
||||
|
||||
import org.hibernate.FlushMode;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.context.internal.JTASessionContext;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
/**
|
||||
* Spring-specific subclass of Hibernate's JTASessionContext,
|
||||
* setting <code>FlushMode.MANUAL</code> for read-only transactions.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
*/
|
||||
public class SpringJtaSessionContext extends JTASessionContext {
|
||||
|
||||
public SpringJtaSessionContext(SessionFactoryImplementor factory) {
|
||||
super(factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Session buildOrObtainSession() {
|
||||
Session session = super.buildOrObtainSession();
|
||||
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
|
||||
session.setFlushMode(FlushMode.MANUAL);
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.orm.hibernate4;
|
||||
|
||||
import javax.transaction.TransactionManager;
|
||||
|
||||
import org.hibernate.FlushMode;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.context.spi.CurrentSessionContext;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.service.jta.platform.spi.JtaPlatform;
|
||||
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
/**
|
||||
* Implementation of Hibernate 3.1's CurrentSessionContext interface
|
||||
* that delegates to Spring's SessionFactoryUtils for providing a
|
||||
* Spring-managed current Session.
|
||||
*
|
||||
* <p>This CurrentSessionContext implementation can also be specified in custom
|
||||
* SessionFactory setup through the "hibernate.current_session_context_class"
|
||||
* property, with the fully qualified name of this class as value.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class SpringSessionContext implements CurrentSessionContext {
|
||||
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
|
||||
private final CurrentSessionContext jtaSessionContext;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new SpringSessionContext for the given Hibernate SessionFactory.
|
||||
* @param sessionFactory the SessionFactory to provide current Sessions for
|
||||
*/
|
||||
public SpringSessionContext(SessionFactoryImplementor sessionFactory) {
|
||||
this.sessionFactory = sessionFactory;
|
||||
JtaPlatform jtaPlatform = sessionFactory.getServiceRegistry().getService(JtaPlatform.class);
|
||||
TransactionManager transactionManager = jtaPlatform.retrieveTransactionManager();
|
||||
this.jtaSessionContext = (transactionManager != null ? new SpringJtaSessionContext(sessionFactory) : null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the Spring-managed Session for the current thread, if any.
|
||||
*/
|
||||
public Session currentSession() throws HibernateException {
|
||||
Object value = TransactionSynchronizationManager.getResource(this.sessionFactory);
|
||||
if (value instanceof Session) {
|
||||
return (Session) value;
|
||||
}
|
||||
else if (value instanceof SessionHolder) {
|
||||
SessionHolder sessionHolder = (SessionHolder) value;
|
||||
Session session = sessionHolder.getSession();
|
||||
if (TransactionSynchronizationManager.isSynchronizationActive() &&
|
||||
!sessionHolder.isSynchronizedWithTransaction()) {
|
||||
TransactionSynchronizationManager.registerSynchronization(
|
||||
new SpringSessionSynchronization(sessionHolder, this.sessionFactory));
|
||||
sessionHolder.setSynchronizedWithTransaction(true);
|
||||
// Switch to FlushMode.AUTO, as we have to assume a thread-bound Session
|
||||
// with FlushMode.MANUAL, which needs to allow flushing within the transaction.
|
||||
FlushMode flushMode = session.getFlushMode();
|
||||
if (FlushMode.isManualFlushMode(flushMode) &&
|
||||
!TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
|
||||
session.setFlushMode(FlushMode.AUTO);
|
||||
sessionHolder.setPreviousFlushMode(flushMode);
|
||||
}
|
||||
}
|
||||
return session;
|
||||
}
|
||||
else if (this.jtaSessionContext != null) {
|
||||
Session session = this.jtaSessionContext.currentSession();
|
||||
if (TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
TransactionSynchronizationManager.registerSynchronization(new SpringFlushSynchronization(session));
|
||||
}
|
||||
return session;
|
||||
}
|
||||
else {
|
||||
throw new HibernateException("No Session found for current thread");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.orm.hibernate4;
|
||||
|
||||
import org.hibernate.FlushMode;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.SessionFactory;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
/**
|
||||
* Callback for resource cleanup at the end of a Spring-managed transaction
|
||||
* for a pre-bound Hibernate Session.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
*/
|
||||
class SpringSessionSynchronization implements TransactionSynchronization, Ordered {
|
||||
|
||||
private final SessionHolder sessionHolder;
|
||||
|
||||
private final SessionFactory sessionFactory;
|
||||
|
||||
private boolean holderActive = true;
|
||||
|
||||
|
||||
public SpringSessionSynchronization(SessionHolder sessionHolder, SessionFactory sessionFactory) {
|
||||
this.sessionHolder = sessionHolder;
|
||||
this.sessionFactory = sessionFactory;
|
||||
}
|
||||
|
||||
private Session getCurrentSession() {
|
||||
return this.sessionHolder.getSession();
|
||||
}
|
||||
|
||||
|
||||
public int getOrder() {
|
||||
return SessionFactoryUtils.SESSION_SYNCHRONIZATION_ORDER;
|
||||
}
|
||||
|
||||
public void suspend() {
|
||||
if (this.holderActive) {
|
||||
TransactionSynchronizationManager.unbindResource(this.sessionFactory);
|
||||
// Eagerly disconnect the Session here, to make release mode "on_close" work on JBoss.
|
||||
getCurrentSession().disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
if (this.holderActive) {
|
||||
TransactionSynchronizationManager.bindResource(this.sessionFactory, this.sessionHolder);
|
||||
}
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
try {
|
||||
SessionFactoryUtils.logger.debug("Flushing Hibernate Session on explicit request");
|
||||
getCurrentSession().flush();
|
||||
}
|
||||
catch (HibernateException ex) {
|
||||
throw SessionFactoryUtils.convertHibernateAccessException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void beforeCommit(boolean readOnly) throws DataAccessException {
|
||||
if (!readOnly) {
|
||||
Session session = getCurrentSession();
|
||||
// Read-write transaction -> flush the Hibernate Session.
|
||||
// Further check: only flush when not FlushMode.MANUAL.
|
||||
if (!FlushMode.isManualFlushMode(session.getFlushMode())) {
|
||||
try {
|
||||
SessionFactoryUtils.logger.debug("Flushing Hibernate Session on transaction synchronization");
|
||||
session.flush();
|
||||
}
|
||||
catch (HibernateException ex) {
|
||||
throw SessionFactoryUtils.convertHibernateAccessException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void beforeCompletion() {
|
||||
Session session = this.sessionHolder.getSession();
|
||||
if (this.sessionHolder.getPreviousFlushMode() != null) {
|
||||
// In case of pre-bound Session, restore previous flush mode.
|
||||
session.setFlushMode(this.sessionHolder.getPreviousFlushMode());
|
||||
}
|
||||
// Eagerly disconnect the Session here, to make release mode "on_close" work nicely.
|
||||
session.disconnect();
|
||||
}
|
||||
|
||||
public void afterCommit() {
|
||||
}
|
||||
|
||||
public void afterCompletion(int status) {
|
||||
try {
|
||||
if (status != STATUS_COMMITTED) {
|
||||
// Clear all pending inserts/updates/deletes in the Session.
|
||||
// Necessary for pre-bound Sessions, to avoid inconsistent state.
|
||||
this.sessionHolder.getSession().clear();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
this.sessionHolder.setSynchronizedWithTransaction(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* Package providing integration of
|
||||
* <a href="http://www.hibernate.org">Hibernate 4.x</a>
|
||||
* with Spring concepts.
|
||||
*
|
||||
* <p>Contains an implementation of Spring's transaction SPI for local Hibernate transactions.
|
||||
* This package is intentionally rather minimal, with no template classes or the like,
|
||||
* in order to follow native Hibernate recommendations as closely as possible.
|
||||
*
|
||||
* <p><b>This package supports Hibernate 4.x only.</b>
|
||||
* See the <code>org.springframework.orm.hibernate3</code> package for Hibernate 3.x support.
|
||||
*
|
||||
*/
|
||||
package org.springframework.orm.hibernate4;
|
||||
|
||||
@@ -1,252 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.orm.hibernate4.support;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.hibernate.FlushMode;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.orm.hibernate4.SessionFactoryUtils;
|
||||
import org.springframework.orm.hibernate4.SessionHolder;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.async.CallableProcessingInterceptorAdapter;
|
||||
import org.springframework.web.context.request.async.WebAsyncManager;
|
||||
import org.springframework.web.context.request.async.WebAsyncUtils;
|
||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
/**
|
||||
* Servlet 2.3 Filter that binds a Hibernate Session to the thread for the entire
|
||||
* processing of the request. Intended for the "Open Session in View" pattern,
|
||||
* i.e. to allow for lazy loading in web views despite the original transactions
|
||||
* already being completed.
|
||||
*
|
||||
* <p>This filter makes Hibernate Sessions available via the current thread, which
|
||||
* will be autodetected by transaction managers. It is suitable for service layer
|
||||
* transactions via {@link org.springframework.orm.hibernate4.HibernateTransactionManager}
|
||||
* as well as for non-transactional execution (if configured appropriately).
|
||||
*
|
||||
* <p><b>NOTE</b>: This filter will by default <i>not</i> flush the Hibernate Session,
|
||||
* with the flush mode set to <code>FlushMode.NEVER</code>. It assumes to be used
|
||||
* in combination with service layer transactions that care for the flushing: The
|
||||
* active transaction manager will temporarily change the flush mode to
|
||||
* <code>FlushMode.AUTO</code> during a read-write transaction, with the flush
|
||||
* mode reset to <code>FlushMode.NEVER</code> at the end of each transaction.
|
||||
*
|
||||
* <p><b>WARNING:</b> Applying this filter to existing logic can cause issues that
|
||||
* have not appeared before, through the use of a single Hibernate Session for the
|
||||
* processing of an entire request. In particular, the reassociation of persistent
|
||||
* objects with a Hibernate Session has to occur at the very beginning of request
|
||||
* processing, to avoid clashes with already loaded instances of the same objects.
|
||||
*
|
||||
* <p>Looks up the SessionFactory in Spring's root web application context.
|
||||
* Supports a "sessionFactoryBeanName" filter init-param in <code>web.xml</code>;
|
||||
* the default bean name is "sessionFactory". Looks up the SessionFactory on each
|
||||
* request, to avoid initialization order issues (when using ContextLoaderServlet,
|
||||
* the root application context will get initialized <i>after</i> this filter).
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see #lookupSessionFactory
|
||||
* @see OpenSessionInViewInterceptor
|
||||
* @see org.springframework.orm.hibernate4.HibernateTransactionManager
|
||||
* @see org.springframework.transaction.support.TransactionSynchronizationManager
|
||||
*/
|
||||
public class OpenSessionInViewFilter extends OncePerRequestFilter {
|
||||
|
||||
public static final String DEFAULT_SESSION_FACTORY_BEAN_NAME = "sessionFactory";
|
||||
|
||||
private String sessionFactoryBeanName = DEFAULT_SESSION_FACTORY_BEAN_NAME;
|
||||
|
||||
|
||||
/**
|
||||
* Set the bean name of the SessionFactory to fetch from Spring's
|
||||
* root application context. Default is "sessionFactory".
|
||||
* @see #DEFAULT_SESSION_FACTORY_BEAN_NAME
|
||||
*/
|
||||
public void setSessionFactoryBeanName(String sessionFactoryBeanName) {
|
||||
this.sessionFactoryBeanName = sessionFactoryBeanName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bean name of the SessionFactory to fetch from Spring's
|
||||
* root application context.
|
||||
*/
|
||||
protected String getSessionFactoryBeanName() {
|
||||
return this.sessionFactoryBeanName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns "false" so that the filter may re-bind the opened Hibernate
|
||||
* {@code Session} to each asynchronously dispatched thread and postpone
|
||||
* closing it until the very last asynchronous dispatch.
|
||||
*/
|
||||
@Override
|
||||
protected boolean shouldNotFilterAsyncDispatch() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns "false" so that the filter may provide a Hibernate
|
||||
* {@code Session} to each error dispatches.
|
||||
*/
|
||||
@Override
|
||||
protected boolean shouldNotFilterErrorDispatch() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(
|
||||
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
|
||||
SessionFactory sessionFactory = lookupSessionFactory(request);
|
||||
boolean participate = false;
|
||||
|
||||
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
|
||||
String key = getAlreadyFilteredAttributeName();
|
||||
|
||||
if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
|
||||
// Do not modify the Session: just set the participate flag.
|
||||
participate = true;
|
||||
}
|
||||
else {
|
||||
boolean isFirstRequest = !isAsyncDispatch(request);
|
||||
if (isFirstRequest || !applySessionBindingInterceptor(asyncManager, key)) {
|
||||
logger.debug("Opening Hibernate Session in OpenSessionInViewFilter");
|
||||
Session session = openSession(sessionFactory);
|
||||
SessionHolder sessionHolder = new SessionHolder(session);
|
||||
TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
|
||||
|
||||
asyncManager.registerCallableInterceptor(key,
|
||||
new SessionBindingCallableInterceptor(sessionFactory, sessionHolder));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
finally {
|
||||
if (!participate) {
|
||||
SessionHolder sessionHolder =
|
||||
(SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
|
||||
if (!isAsyncStarted(request)) {
|
||||
logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");
|
||||
SessionFactoryUtils.closeSession(sessionHolder.getSession());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up the SessionFactory that this filter should use,
|
||||
* taking the current HTTP request as argument.
|
||||
* <p>The default implementation delegates to the {@link #lookupSessionFactory()}
|
||||
* variant without arguments.
|
||||
* @param request the current request
|
||||
* @return the SessionFactory to use
|
||||
*/
|
||||
protected SessionFactory lookupSessionFactory(HttpServletRequest request) {
|
||||
return lookupSessionFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up the SessionFactory that this filter should use.
|
||||
* <p>The default implementation looks for a bean with the specified name
|
||||
* in Spring's root application context.
|
||||
* @return the SessionFactory to use
|
||||
* @see #getSessionFactoryBeanName
|
||||
*/
|
||||
protected SessionFactory lookupSessionFactory() {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Using SessionFactory '" + getSessionFactoryBeanName() + "' for OpenSessionInViewFilter");
|
||||
}
|
||||
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
|
||||
return wac.getBean(getSessionFactoryBeanName(), SessionFactory.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a Session for the SessionFactory that this filter uses.
|
||||
* <p>The default implementation delegates to the
|
||||
* <code>SessionFactory.openSession</code> method and
|
||||
* sets the <code>Session</code>'s flush mode to "MANUAL".
|
||||
* @param sessionFactory the SessionFactory that this filter uses
|
||||
* @return the Session to use
|
||||
* @throws DataAccessResourceFailureException if the Session could not be created
|
||||
* @see org.hibernate.FlushMode#MANUAL
|
||||
*/
|
||||
protected Session openSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
|
||||
try {
|
||||
Session session = SessionFactoryUtils.openSession(sessionFactory);
|
||||
session.setFlushMode(FlushMode.MANUAL);
|
||||
return session;
|
||||
}
|
||||
catch (HibernateException ex) {
|
||||
throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean applySessionBindingInterceptor(WebAsyncManager asyncManager, String key) {
|
||||
if (asyncManager.getCallableInterceptor(key) == null) {
|
||||
return false;
|
||||
}
|
||||
((SessionBindingCallableInterceptor) asyncManager.getCallableInterceptor(key)).initializeThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Bind and unbind the Hibernate {@code Session} to the current thread.
|
||||
*/
|
||||
private static class SessionBindingCallableInterceptor extends CallableProcessingInterceptorAdapter {
|
||||
|
||||
private final SessionFactory sessionFactory;
|
||||
|
||||
private final SessionHolder sessionHolder;
|
||||
|
||||
public SessionBindingCallableInterceptor(SessionFactory sessionFactory, SessionHolder sessionHolder) {
|
||||
this.sessionFactory = sessionFactory;
|
||||
this.sessionHolder = sessionHolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void preProcess(NativeWebRequest request, Callable<T> task) {
|
||||
initializeThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {
|
||||
TransactionSynchronizationManager.unbindResource(this.sessionFactory);
|
||||
}
|
||||
|
||||
private void initializeThread() {
|
||||
TransactionSynchronizationManager.bindResource(this.sessionFactory, this.sessionHolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.orm.hibernate4.support;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.hibernate.FlushMode;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.orm.hibernate4.SessionFactoryUtils;
|
||||
import org.springframework.orm.hibernate4.SessionHolder;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.context.request.AsyncWebRequestInterceptor;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.context.request.async.CallableProcessingInterceptorAdapter;
|
||||
import org.springframework.web.context.request.async.WebAsyncManager;
|
||||
import org.springframework.web.context.request.async.WebAsyncUtils;
|
||||
|
||||
/**
|
||||
* Spring web request interceptor that binds a Hibernate <code>Session</code> to the
|
||||
* thread for the entire processing of the request.
|
||||
*
|
||||
* <p>This class is a concrete expression of the "Open Session in View" pattern, which
|
||||
* is a pattern that allows for the lazy loading of associations in web views despite
|
||||
* the original transactions already being completed.
|
||||
*
|
||||
* <p>This interceptor makes Hibernate Sessions available via the current thread,
|
||||
* which will be autodetected by transaction managers. It is suitable for service layer
|
||||
* transactions via {@link org.springframework.orm.hibernate4.HibernateTransactionManager}
|
||||
* as well as for non-transactional execution (if configured appropriately).
|
||||
*
|
||||
* <p><b>NOTE</b>: This interceptor will by default <i>not</i> flush the Hibernate
|
||||
* <code>Session</code>, with the flush mode being set to <code>FlushMode.NEVER</code>.
|
||||
* It assumes that it will be used in combination with service layer transactions
|
||||
* that handle the flushing: the active transaction manager will temporarily change
|
||||
* the flush mode to <code>FlushMode.AUTO</code> during a read-write transaction,
|
||||
* with the flush mode reset to <code>FlushMode.NEVER</code> at the end of each
|
||||
* transaction. If you intend to use this interceptor without transactions, consider
|
||||
* changing the default flush mode (through the {@link #setFlushMode "flushMode"} property).
|
||||
*
|
||||
* <p>In contrast to {@link OpenSessionInViewFilter}, this interceptor is configured
|
||||
* in a Spring application context and can thus take advantage of bean wiring..
|
||||
*
|
||||
* <p><b>WARNING:</b> Applying this interceptor to existing logic can cause issues
|
||||
* that have not appeared before, through the use of a single Hibernate
|
||||
* <code>Session</code> for the processing of an entire request. In particular, the
|
||||
* reassociation of persistent objects with a Hibernate <code>Session</code> has to
|
||||
* occur at the very beginning of request processing, to avoid clashes with already
|
||||
* loaded instances of the same objects.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see #setSingleSession
|
||||
* @see #setFlushMode
|
||||
* @see OpenSessionInViewFilter
|
||||
* @see org.springframework.orm.hibernate4.HibernateTransactionManager
|
||||
* @see org.springframework.transaction.support.TransactionSynchronizationManager
|
||||
*/
|
||||
public class OpenSessionInViewInterceptor implements AsyncWebRequestInterceptor {
|
||||
|
||||
/**
|
||||
* Suffix that gets appended to the <code>SessionFactory</code>
|
||||
* <code>toString()</code> representation for the "participate in existing
|
||||
* session handling" request attribute.
|
||||
* @see #getParticipateAttributeName
|
||||
*/
|
||||
public static final String PARTICIPATE_SUFFIX = ".PARTICIPATE";
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private SessionFactory sessionFactory;
|
||||
|
||||
|
||||
public void setSessionFactory(SessionFactory sessionFactory) {
|
||||
this.sessionFactory = sessionFactory;
|
||||
}
|
||||
|
||||
public SessionFactory getSessionFactory() {
|
||||
return this.sessionFactory;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Open a new Hibernate <code>Session</code> according to the settings of this
|
||||
* <code>HibernateAccessor</code> and bind it to the thread via the
|
||||
* {@link org.springframework.transaction.support.TransactionSynchronizationManager}.
|
||||
*/
|
||||
public void preHandle(WebRequest request) throws DataAccessException {
|
||||
|
||||
String participateAttributeName = getParticipateAttributeName();
|
||||
|
||||
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
|
||||
if (asyncManager.hasConcurrentResult()) {
|
||||
if (applySessionBindingInterceptor(asyncManager, participateAttributeName)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (TransactionSynchronizationManager.hasResource(getSessionFactory())) {
|
||||
// Do not modify the Session: just mark the request accordingly.
|
||||
Integer count = (Integer) request.getAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
|
||||
int newCount = (count != null ? count + 1 : 1);
|
||||
request.setAttribute(getParticipateAttributeName(), newCount, WebRequest.SCOPE_REQUEST);
|
||||
}
|
||||
else {
|
||||
logger.debug("Opening Hibernate Session in OpenSessionInViewInterceptor");
|
||||
Session session = openSession();
|
||||
SessionHolder sessionHolder = new SessionHolder(session);
|
||||
TransactionSynchronizationManager.bindResource(getSessionFactory(), sessionHolder);
|
||||
|
||||
asyncManager.registerCallableInterceptor(participateAttributeName,
|
||||
new SessionBindingCallableInterceptor(sessionHolder));
|
||||
}
|
||||
}
|
||||
|
||||
public void postHandle(WebRequest request, ModelMap model) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbind the Hibernate <code>Session</code> from the thread and close it).
|
||||
* @see org.springframework.transaction.support.TransactionSynchronizationManager
|
||||
*/
|
||||
public void afterCompletion(WebRequest request, Exception ex) throws DataAccessException {
|
||||
if (!decrementParticipateCount(request)) {
|
||||
SessionHolder sessionHolder =
|
||||
(SessionHolder) TransactionSynchronizationManager.unbindResource(getSessionFactory());
|
||||
logger.debug("Closing Hibernate Session in OpenSessionInViewInterceptor");
|
||||
SessionFactoryUtils.closeSession(sessionHolder.getSession());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private boolean decrementParticipateCount(WebRequest request) {
|
||||
String participateAttributeName = getParticipateAttributeName();
|
||||
Integer count = (Integer) request.getAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
|
||||
if (count == null) {
|
||||
return false;
|
||||
}
|
||||
// Do not modify the Session: just clear the marker.
|
||||
if (count > 1) {
|
||||
request.setAttribute(participateAttributeName, count - 1, WebRequest.SCOPE_REQUEST);
|
||||
}
|
||||
else {
|
||||
request.removeAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void afterConcurrentHandlingStarted(WebRequest request) {
|
||||
if (!decrementParticipateCount(request)) {
|
||||
TransactionSynchronizationManager.unbindResource(getSessionFactory());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a Session for the SessionFactory that this interceptor uses.
|
||||
* <p>The default implementation delegates to the
|
||||
* <code>SessionFactory.openSession</code> method and
|
||||
* sets the <code>Session</code>'s flush mode to "MANUAL".
|
||||
* @return the Session to use
|
||||
* @throws DataAccessResourceFailureException if the Session could not be created
|
||||
* @see org.hibernate.FlushMode#MANUAL
|
||||
*/
|
||||
protected Session openSession() throws DataAccessResourceFailureException {
|
||||
try {
|
||||
Session session = SessionFactoryUtils.openSession(getSessionFactory());
|
||||
session.setFlushMode(FlushMode.MANUAL);
|
||||
return session;
|
||||
}
|
||||
catch (HibernateException ex) {
|
||||
throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the request attribute that identifies that a request is
|
||||
* already intercepted.
|
||||
* <p>The default implementation takes the <code>toString()</code> representation
|
||||
* of the <code>SessionFactory</code> instance and appends {@link #PARTICIPATE_SUFFIX}.
|
||||
*/
|
||||
protected String getParticipateAttributeName() {
|
||||
return getSessionFactory().toString() + PARTICIPATE_SUFFIX;
|
||||
}
|
||||
|
||||
private boolean applySessionBindingInterceptor(WebAsyncManager asyncManager, String key) {
|
||||
if (asyncManager.getCallableInterceptor(key) == null) {
|
||||
return false;
|
||||
}
|
||||
((SessionBindingCallableInterceptor) asyncManager.getCallableInterceptor(key)).initializeThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Bind and unbind the Hibernate {@code Session} to the current thread.
|
||||
*/
|
||||
private class SessionBindingCallableInterceptor extends CallableProcessingInterceptorAdapter {
|
||||
|
||||
private final SessionHolder sessionHolder;
|
||||
|
||||
public SessionBindingCallableInterceptor(SessionHolder sessionHolder) {
|
||||
this.sessionHolder = sessionHolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void preProcess(NativeWebRequest request, Callable<T> task) {
|
||||
initializeThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {
|
||||
TransactionSynchronizationManager.unbindResource(getSessionFactory());
|
||||
}
|
||||
|
||||
private void initializeThread() {
|
||||
TransactionSynchronizationManager.bindResource(getSessionFactory(), this.sessionHolder);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* Classes supporting the <code>org.springframework.orm.hibernate4</code> package.
|
||||
*
|
||||
*/
|
||||
package org.springframework.orm.hibernate4.support;
|
||||
|
||||
Reference in New Issue
Block a user