HibernateTransactionManager for Hibernate 4 supports "entityInterceptor(BeanName)" property

Issue: SPR-10301
This commit is contained in:
Juergen Hoeller
2013-02-27 00:22:18 +01:00
parent 0d69a630ad
commit 096972d2b1
2 changed files with 470 additions and 628 deletions

View File

@@ -22,12 +22,16 @@ import javax.sql.DataSource;
import org.hibernate.ConnectionReleaseMode; import org.hibernate.ConnectionReleaseMode;
import org.hibernate.FlushMode; import org.hibernate.FlushMode;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.SessionFactory; import org.hibernate.SessionFactory;
import org.hibernate.Transaction; import org.hibernate.Transaction;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.transaction.spi.TransactionContext; import org.hibernate.engine.transaction.spi.TransactionContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.dao.DataAccessResourceFailureException;
@@ -100,7 +104,7 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
*/ */
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class HibernateTransactionManager extends AbstractPlatformTransactionManager public class HibernateTransactionManager extends AbstractPlatformTransactionManager
implements ResourceTransactionManager, InitializingBean { implements ResourceTransactionManager, BeanFactoryAware, InitializingBean {
private SessionFactory sessionFactory; private SessionFactory sessionFactory;
@@ -112,6 +116,14 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
private boolean hibernateManagedSession = false; private boolean hibernateManagedSession = false;
private Object entityInterceptor;
/**
* Just needed for entityInterceptorBeanName.
* @see #setEntityInterceptorBeanName
*/
private BeanFactory beanFactory;
/** /**
* Create a new HibernateTransactionManager instance. * Create a new HibernateTransactionManager instance.
@@ -229,7 +241,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
* to always return a proper Session when called for a Spring-managed transaction; * to always return a proper Session when called for a Spring-managed transaction;
* transaction begin will fail if the {@code getCurrentSession()} call fails. * transaction begin will fail if the {@code getCurrentSession()} call fails.
* <p>This mode will typically be used in combination with a custom Hibernate * <p>This mode will typically be used in combination with a custom Hibernate
* {@link org.hibernate.context.CurrentSessionContext} implementation that stores * {@link org.hibernate.context.spi.CurrentSessionContext} implementation that stores
* Sessions in a place other than Spring's TransactionSynchronizationManager. * Sessions in a place other than Spring's TransactionSynchronizationManager.
* It may also be used in combination with Spring's Open-Session-in-View support * 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 * (using Spring's default {@link SpringSessionContext}), in which case it subtly
@@ -242,10 +254,81 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
this.hibernateManagedSession = hibernateManagedSession; this.hibernateManagedSession = hibernateManagedSession;
} }
/**
* Set the bean name of 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 transaction manager.
* <p>Requires the bean factory to be known, to be able to resolve the bean
* name to an interceptor instance on session creation. Typically used for
* prototype interceptors, i.e. a new interceptor instance per session.
* <p>Can also be used for shared interceptor instances, but it is recommended
* to set the interceptor reference directly in such a scenario.
* @param entityInterceptorBeanName the name of the entity interceptor in
* the bean factory
* @see #setBeanFactory
* @see #setEntityInterceptor
*/
public void setEntityInterceptorBeanName(String entityInterceptorBeanName) {
this.entityInterceptor = entityInterceptorBeanName;
}
/**
* 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 transaction manager.
* <p>Such an interceptor can either be set at the SessionFactory level,
* i.e. on LocalSessionFactoryBean, or at the Session level, i.e. on
* HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager.
* It's preferable to set it on LocalSessionFactoryBean or HibernateTransactionManager
* to avoid repeated configuration and guarantee consistent behavior in transactions.
* @see LocalSessionFactoryBean#setEntityInterceptor
*/
public void setEntityInterceptor(Interceptor entityInterceptor) {
this.entityInterceptor = entityInterceptor;
}
/**
* Return the current Hibernate entity interceptor, or {@code null} if none.
* Resolves an entity interceptor bean name via the bean factory,
* if necessary.
* @throws IllegalStateException if bean name specified but no bean factory set
* @throws BeansException if bean name resolution via the bean factory failed
* @see #setEntityInterceptor
* @see #setEntityInterceptorBeanName
* @see #setBeanFactory
*/
public Interceptor getEntityInterceptor() throws IllegalStateException, BeansException {
if (this.entityInterceptor instanceof Interceptor) {
return (Interceptor) entityInterceptor;
}
else if (this.entityInterceptor instanceof String) {
if (this.beanFactory == null) {
throw new IllegalStateException("Cannot get entity interceptor via bean name if no bean factory set");
}
String beanName = (String) this.entityInterceptor;
return this.beanFactory.getBean(beanName, Interceptor.class);
}
else {
return null;
}
}
/**
* The bean factory just needs to be known for resolving entity interceptor
* bean names. It does not need to be set for any other mode of operation.
* @see #setEntityInterceptorBeanName
*/
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public void afterPropertiesSet() { public void afterPropertiesSet() {
if (getSessionFactory() == null) { if (getSessionFactory() == null) {
throw new IllegalArgumentException("Property 'sessionFactory' is required"); throw new IllegalArgumentException("Property 'sessionFactory' is required");
} }
if (this.entityInterceptor instanceof String && this.beanFactory == null) {
throw new IllegalArgumentException("Property 'beanFactory' is required for 'entityInterceptorBeanName'");
}
// Check for SessionFactory's DataSource. // Check for SessionFactory's DataSource.
if (this.autodetectDataSource && getDataSource() == null) { if (this.autodetectDataSource && getDataSource() == null) {
@@ -325,7 +408,10 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
try { try {
if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) { if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) {
Session newSession = getSessionFactory().openSession(); Interceptor entityInterceptor = getEntityInterceptor();
Session newSession = (entityInterceptor != null ?
getSessionFactory().withOptions().interceptor(entityInterceptor).openSession() :
getSessionFactory().openSession());
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Opened new Session [" + newSession + "] for Hibernate transaction"); logger.debug("Opened new Session [" + newSession + "] for Hibernate transaction");
} }