Skip to content

Commit

Permalink
HibernateTransactionManager for Hibernate 4 supports "entityIntercept…
Browse files Browse the repository at this point in the history
…or(BeanName)" property

Issue: SPR-10301
  • Loading branch information
jhoeller committed Feb 26, 2013
1 parent 0d69a63 commit 096972d
Show file tree
Hide file tree
Showing 2 changed files with 484 additions and 642 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,16 @@
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;
Expand Down Expand Up @@ -100,7 +104,7 @@
*/
@SuppressWarnings("serial")
public class HibernateTransactionManager extends AbstractPlatformTransactionManager
implements ResourceTransactionManager, InitializingBean {
implements ResourceTransactionManager, BeanFactoryAware, InitializingBean {

private SessionFactory sessionFactory;

Expand All @@ -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.
Expand Down Expand Up @@ -229,7 +241,7 @@ public void setPrepareConnection(boolean prepareConnection) {
* 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
Expand All @@ -242,10 +254,81 @@ public void setHibernateManagedSession(boolean 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() {
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) {
Expand Down Expand Up @@ -325,7 +408,10 @@ protected void doBegin(Object transaction, TransactionDefinition definition) {

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");
}
Expand Down
Loading

0 comments on commit 096972d

Please sign in to comment.