Problem
In this tutorial you will learn how to resolve CannotCreateTransactionException when you have multiple transaction managers in context file.org.springframework.transaction.CannotCreateTransactionException:
Could not open Hibernate Session for transaction;
nested exception is java.lang.IllegalStateException: Already value
[org.springframework.jdbc.datasource.ConnectionHolder@ebac27] for key
[org.apache.tomcat.dbcp.dbcp2.BasicDataSource@9266ae] bound to thread
[http-nio-8080-exec-10]
public class MyServiceImpl implements MyService { public void method1() { //code here } public void method2() { //code here } }
This normally happens when you have multiple transaction managers in place. Let us explain it in more detail about having multiple transaction managers in applicationContext.xml file.
First TransactionManager
<tx:annotaion-driven /> <bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"/> </bean>
When you define <tx:annotaion-driven /> and @Transactional annotation, usually it will target bean named transactionManager i.e HibernateTransactionManager.
HibernateTransactionManager ,
Second TransactionManagerBinds a Hibernate Session from the specified factory to the thread, potentially allowing for one thread-bound Session per factory.SessionFactory.getCurrentSession()
is required for Hibernate access code that needs to support this transaction handling mechanism, with the SessionFactory being configured withSpringSessionContext
.
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="get*" read-only="true"/> <tx:method name="*"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="serviceOperation" expression=" execution(* com.myapp.service.MyServiceImpl.*(..)) || -------------------------- <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation"/> </aop:config>
The error comes from DataSourceTransactionManager :
TransactionSynchronizationManager.bindResource(getDataSource(), connectionHolder);
i.e DataSourceTransactionManager is trying to bind the datasource (not the Hibernate Session) to the thread, but the datasource is already there and this is unexpected.
Note that the transaction manager had not started yet to bind the Hibernate Session to the thread, that would happen next at HibernateTransactionManager :
There are two possible explanations:
- Somebody (another transaction manager?) is adding the datasource to the thread before the transaction manager and this is unexpected.
- Or none is adding the datasource to the transaction manager, is just that at the end of the task execution none cleans the thread before returning it to the pool, maybe due an error or an unhandled exception.
Solution
@Transactional public class MyServiceImpl implements MyService { public void method1() { //code here } public void method2() { //code here } }
Or without using transactional manager in applicationContext.xml file, use @TransactionalManager annotation in service class.
@Transactional @TransactionalManager("txManager") public class MyServiceImpl implements MyService { public void method1() { //code here } public void method2() { //code here } }
No comments:
Post a Comment