Quartz and hibernate session

Hibernate sessions that are initiated from the web layer are covered by the Spring OpenEntityManagerInViewInterceptor. But what about asynchronous executions, or jobs triggered by a scheduler like Quartz?

Instead of dealing with TransactionSynchronizationManager or worse, hibernate in your Quartz code, it would be nice to have an elegant transparant solution like the OpenEntityManagerInViewInterceptor.

With a little work (or actually almost no work) this is possible. Instead of binding to the web layer, we can create an aop advisor to intercept method calls to a task class. Put that task class in the jobdata of your quartz job and call it. The OpenEntityManagerInViewMethodInterceptor (code below) will transparently do the work for you.

<bean id="reindexTask" class="org.springframework.aop.framework.ProxyFactoryBean">
  <property name="target">
    <bean class="nl.avisi.SomeTask"/>
  </property>
  <property name="interceptorNames">
    <list>
      <value>OpenEntityManagerInViewMethodInterceptor</value>
    </list>
  </property>
</bean>
public class OpenEntityManagerInViewMethodInterceptor extends EntityManagerFactoryAccessor implements Advisor {

    public void pre() throws DataAccessException {
            logger.debug("Opening JPA EntityManager in OpenEntityManagerInViewInterceptor");
            try {
                EntityManager em = createEntityManager();
                TransactionSynchronizationManager.bindResource(getEntityManagerFactory(), new EntityManagerHolder(em));
            }
            catch (PersistenceException ex) {
                throw new DataAccessResourceFailureException("Could not create JPA EntityManager", ex);
            }
    }

    public void post() throws DataAccessException {
            EntityManagerHolder emHolder = (EntityManagerHolder)
                    TransactionSynchronizationManager.unbindResource(getEntityManagerFactory());
            logger.debug("Closing JPA EntityManager in OpenEntityManagerInViewInterceptor");
            EntityManagerFactoryUtils.closeEntityManager(emHolder.getEntityManager());
    }

    @Override
    public Advice getAdvice() {
        return new MethodInterceptor() {

            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {

                if (invocation.getMethod().isAnnotationPresent(Transactional.class)) {
                    pre();
                }
                Object answer = invocation.proceed();
                if (invocation.getMethod().isAnnotationPresent(Transactional.class)) {
                    post();
                }
                return answer;
            }
        };

    }

    @Override
    public boolean isPerInstance() {
        return false;
    }

}

The check for an existing annotation can be left out, it is a quick way to select a single method to be intercepted. I annotate that method with:

@Transactional(readOnly=true)

Additionally you can also check for existing bindings and use these when they are already present.

  • Facebook
  • LinkedIn

Leave a Reply

Your email address will not be published. Required fields are marked *

*

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>