This forum is now a read-only archive. All commenting, posting, registration services have been turned off. Those needing community support and/or wanting to ask questions should refer to the Tag/Forum map, and to http://spring.io/questions for a curated list of stackoverflow tags that Pivotal engineers, and the community, monitor.

custom callback before a transaction is committed

Jun 26th, 2007, 12:01 AM

Hi,
I'm using JTATransactionManager and JDK 5 annotations to manage transactions with hibernate, using Spring 2.0.5.

I would like to be able to hook in some custom code just before the database commit is called. I could use the hibernate event listener framework, but that is called on updates to the individual POJO entities - I want my code to run just once towards the end of the transaction.

As an added bit of complexity - my code must run within the transaction - i.e., it does some database work (via hibernate again) of its own.

I attempted doing this with AOP, wiring an aspect around AbstractPlatformTransactionManager.commit(Transact ionStatus) but this resulted in a circular depency at spring startup around the hibernate session factory bean.

I agree with Karl AOP is the way to go. You could write an MethodInterceptor and make sure that that is executed after the TransactionInterceptor (make it Ordered and set the attribute lower then the Transaction order). That way it will be called after the start of your transaction and before the commit of the transaction.

Comment

If you are using Spring transaction manager (you don't say explicitly, but I assume you are), have you looked at TransactionSynchronization and TransactionSynchronizationManager? AOP can still be part of the solution if the concern is cross-cutting, but since the transaction synchronizations are thread local, you can set them up from anywhere inside a transaction.

Comment

Well, the transaction interceptor is a bit too early. Remember that I want to intercept just before the transaction is commited (or rolled back as the case may be). The transaction interceptor is fired multiple times in the life-cycle of a use-case as multiple services that have been marked as @Transactional are called within a single-use case.

What I'd like to do is intercept at AbstractPlatformTransactionManager.commit(Transact ionStatus) (and the equivalent rollback method) so that my custom code is guaranteed to be called once and only once just before a database commit/rollback is accomplished.

The problem I get when trying to intercept those two methods in the transaction manager is a bean creation exception when applying an aspect to the methods. This happens whether I use the @Aspect way or the older Spring 1.x AOP API.

Here's the stack trace:

Cannot resolve reference to bean 'transactionManager' while setting bean property 'transactionManager'; nested exception is org.springframework.beans.factory.BeanCurrentlyInC reationException: Error creating bean with name 'transactionManager': Bean with name 'transactionManager' has been injected into other beans [(inner bean)] in its raw version as part of a circular reference, but has eventually been wrapped (for example as part of auto-proxy creation). This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
Caused by: org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'transactionInterceptor' defined in URL [file:/Users/ishaaq/IdeaProjects/admomentum/admanager/config/admanager-appcontext-persistence.xml]: Cannot resolve reference to bean 'transactionManager' while setting bean property 'transactionManager'; nested exception is org.springframework.beans.factory.BeanCurrentlyInC reationException: Error creating bean with name 'transactionManager': Bean with name 'transactionManager' has been injected into other beans [(inner bean)] in its raw version as part of a circular reference, but has eventually been wrapped (for example as part of auto-proxy creation). This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
Caused by: org.springframework.beans.factory.BeanCurrentlyInC reationException: Error creating bean with name 'transactionManager': Bean with name 'transactionManager' has been injected into other beans [(inner bean)] in its raw version as part of a circular reference, but has eventually been wrapped (for example as part of auto-proxy creation). This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
at org.springframework.beans.factory.support.Abstract AutowireCapableBeanFactory.createBean(AbstractAuto wireCapableBeanFactory.java:432)
at org.springframework.beans.factory.support.Abstract BeanFactory$1.getObject(AbstractBeanFactory.java:2 51)
at org.springframework.beans.factory.support.DefaultS ingletonBeanRegistry.getSingleton(DefaultSingleton BeanRegistry.java:156)
at org.springframework.beans.factory.support.Abstract BeanFactory.getBean(AbstractBeanFactory.java:248)
at org.springframework.beans.factory.support.Abstract BeanFactory.getBean(AbstractBeanFactory.java:160)
at org.springframework.beans.factory.support.BeanDefi nitionValueResolver.resolveReference(BeanDefinitio nValueResolver.java:261)
at org.springframework.beans.factory.support.BeanDefi nitionValueResolver.resolveValueIfNecessary(BeanDe finitionValueResolver.java:109)
at org.springframework.beans.factory.support.Abstract AutowireCapableBeanFactory.applyPropertyValues(Abs tractAutowireCapableBeanFactory.java:1100)
at org.springframework.beans.factory.support.Abstract AutowireCapableBeanFactory.populateBean(AbstractAu towireCapableBeanFactory.java:862)
at org.springframework.beans.factory.support.Abstract AutowireCapableBeanFactory.createBean(AbstractAuto wireCapableBeanFactory.java:424)
at org.springframework.beans.factory.support.Abstract BeanFactory$1.getObject(AbstractBeanFactory.java:2 51)
at org.springframework.beans.factory.support.DefaultS ingletonBeanRegistry.getSingleton(DefaultSingleton BeanRegistry.java:156)
at org.springframework.beans.factory.support.Abstract BeanFactory.getBean(AbstractBeanFactory.java:248)
at org.springframework.beans.factory.support.Abstract BeanFactory.getBean(AbstractBeanFactory.java:160)
at org.springframework.beans.factory.support.BeanDefi nitionValueResolver.resolveReference(BeanDefinitio nValueResolver.java:261)
at org.springframework.beans.factory.support.BeanDefi nitionValueResolver.resolveValueIfNecessary(BeanDe finitionValueResolver.java:109)
at org.springframework.beans.factory.support.Abstract AutowireCapableBeanFactory.applyPropertyValues(Abs tractAutowireCapableBeanFactory.java:1100)
at org.springframework.beans.factory.support.Abstract AutowireCapableBeanFactory.populateBean(AbstractAu towireCapableBeanFactory.java:862)
at org.springframework.beans.factory.support.Abstract AutowireCapableBeanFactory.createBean(AbstractAuto wireCapableBeanFactory.java:424)
at org.springframework.beans.factory.support.Abstract BeanFactory$1.getObject(AbstractBeanFactory.java:2 51)
at org.springframework.beans.factory.support.DefaultS ingletonBeanRegistry.getSingleton(DefaultSingleton BeanRegistry.java:156)
at org.springframework.beans.factory.support.Abstract BeanFactory.getBean(AbstractBeanFactory.java:248)
at org.springframework.beans.factory.support.Abstract BeanFactory.getBean(AbstractBeanFactory.java:160)
at org.springframework.aop.framework.autoproxy.BeanFa ctoryAdvisorRetrievalHelper.findAdvisorBeans(BeanF actoryAdvisorRetrievalHelper.java:87)
at org.springframework.aop.framework.autoproxy.Abstra ctAdvisorAutoProxyCreator.findCandidateAdvisors(Ab stractAdvisorAutoProxyCreator.java:96)
at org.springframework.aop.framework.autoproxy.Abstra ctAdvisorAutoProxyCreator.findEligibleAdvisors(Abs tractAdvisorAutoProxyCreator.java:83)
at org.springframework.aop.framework.autoproxy.Abstra ctAdvisorAutoProxyCreator.getAdvicesAndAdvisorsFor Bean(AbstractAdvisorAutoProxyCreator.java:66)
at org.springframework.aop.framework.autoproxy.Abstra ctAutoProxyCreator.postProcessAfterInitialization( AbstractAutoProxyCreator.java:296)
at org.springframework.beans.factory.support.Abstract AutowireCapableBeanFactory.applyBeanPostProcessors AfterInitialization(AbstractAutowireCapableBeanFac tory.java:315)
at org.springframework.beans.factory.support.Abstract AutowireCapableBeanFactory.initializeBean(Abstract AutowireCapableBeanFactory.java:1181)
at org.springframework.beans.factory.support.Abstract AutowireCapableBeanFactory.createBean(AbstractAuto wireCapableBeanFactory.java:428)
at org.springframework.beans.factory.support.Abstract BeanFactory$1.getObject(AbstractBeanFactory.java:2 51)
at org.springframework.beans.factory.support.DefaultS ingletonBeanRegistry.getSingleton(DefaultSingleton BeanRegistry.java:156)
at org.springframework.beans.factory.support.Abstract BeanFactory.getBean(AbstractBeanFactory.java:248)
at org.springframework.beans.factory.support.Abstract BeanFactory.getBean(AbstractBeanFactory.java:160)
at org.springframework.context.support.AbstractApplic ationContext.getBean(AbstractApplicationContext.ja va:733)
at org.springframework.context.support.AbstractApplic ationContext.registerBeanPostProcessors(AbstractAp plicationContext.java:511)
at org.springframework.context.support.AbstractApplic ationContext.refresh(AbstractApplicationContext.ja va:337)
at org.springframework.test.AbstractSingleSpringConte xtTests.createApplicationContext(AbstractSingleSpr ingContextTests.java:199)
at org.springframework.test.AbstractSingleSpringConte xtTests.loadContextLocations(AbstractSingleSpringC ontextTests.java:179)
at org.springframework.test.AbstractSingleSpringConte xtTests.loadContext(AbstractSingleSpringContextTes ts.java:158)
at org.springframework.test.AbstractSpringContextTest s.getContext(AbstractSpringContextTests.java:105)
at org.springframework.test.AbstractSingleSpringConte xtTests.setUp(AbstractSingleSpringContextTests.jav a:87)
at org.springframework.test.ConditionalTestCase.runBa re(ConditionalTestCase.java:69)
at com.intellij.rt.execution.junit.JUnitStarter.main( JUnitStarter.java:40)

Comment

In reply to your question Dave - I am using Spring-based transaction management. Specifically, the JtaTransactionManager.

I did see references to TransactionSynchronizationManager in the source code for AbstractPlatformTransactionManager and the thought did cross my mind that that would be a great place to put my code but I don't know how to set it up. I have a feeling I should be using TransactionSynchronizationManager.registerSynchron ization() but have no idea at what point I should do this. The documentation is silent on this (or I couldn't find anything). Any pointers?

On reading the spring documentation I found (note 2 in section 3.7.1) that since I'm using the @Transactional annotations that are using AOP anyway, the dependent transactionManager bean in not eligible for auto-proxying. So, I can't use AOP for what I want to do. Aaargh!

Ok, so now the only possibility I have is Dave's suggestion of using the TransactionSynchronizationManager.