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.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
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.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
@ -100,7 +104,7 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
*/
@SuppressWarnings("serial")
public class HibernateTransactionManager extends AbstractPlatformTransactionManager
implements ResourceTransactionManager, InitializingBean {
implements ResourceTransactionManager, BeanFactoryAware, InitializingBean {
private SessionFactory sessionFactory;
@ -112,6 +116,14 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
private boolean hibernateManagedSession = false;
private Object entityInterceptor;
/**
* Just needed for entityInterceptorBeanName.
* @see #setEntityInterceptorBeanName
*/
private BeanFactory beanFactory;
/**
* 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;
* transaction begin will fail if the {@code getCurrentSession()} call fails.
* <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.
* 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
@ -242,10 +254,81 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
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() {
if (getSessionFactory() == null) {
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.
if (this.autodetectDataSource && getDataSource() == null) {
@ -325,7 +408,10 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
try {
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()) {
logger.debug("Opened new Session [" + newSession + "] for Hibernate transaction");
}