Spring to automatically open another transactionManager?
I have two Databases , with two sets of spring configurations :
The lower level is CORE
db , the upper level is APP
db .
Each db has its persistenceUnit , entityManagerFactory , transactionManager , with the db name appended , such as "entityManagerFactoryApp" , "transactionManagerCore" ...
Now , I have a Service class , wrapping some DAOs in APP , and some in CORE . But I found I cannot commit CORE's DAOs in my test :
Here is my Service class :
@Inject private AppDao appDao;
@Inject private CoreDao coreDao;
@Override
@Transactional
public void someMethod(foo bar)
{
appDao.save(...); //success
coreDao.save(...); //failed !
}
And it is my test class :
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:app.xml"})
@TransactionConfiguration(transactionManager="transactionManagerApp" , defaultRollback=false)
public class ServiceTest
{
@Inject private Service service;
@Test
@Transactional
public void testSomeMethod()
{
service.someMethod(...);
}
}
I know the reason I cannot commit CORE's DAO , is because the test class's @TransactionConfiguration is "transactionManagerApp
" , not "transactionManagerCore
" .
So , any CREATE/UPDATE/DELETE actions in CORE's DAOs will not be committed. But I cannot enable two txManagers simultaneously (is there any way ?).
So , I modify my service class :
@Inject
@Qualifier("entityManagerFactoryCore")
private EntityManagerFactory emfCore;
@Override
@Transactional
public void someMethod(foo bar)
{
appDao.save(...); //success
Session session = (Session) EntityManagerFactoryUtils.getTransactionalEntityManager(emfCore).getDelegate();
Transaction tx = session.beginTransaction();
coreDao.save(...); //success
tx.commit();
}
Yes , it works ! But that's NOT what I want! Because it introduces a lot of redundant codes (session , tx , commit...).
And ... there is another way , to remove the session / EntityManagerFactoryUtils from Service , and move them to the test class :
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:app.xml"})
@TransactionConfiguration(transactionManager="transactionManagerApp" , defaultRollback=false)
public class ServiceTest
{
@Inject private Service service;
@Inject
@Qualifier("entityManagerFactoryCore")
private EntityManagerFactory emfCore;
@Test
@Transactional
public void testSomeMethod()
{
Session session = (Session) EntityManagerFactoryUtils.getTransactionalEntityManager(emfCore).getDelegate();
Transaction tx = session.beginTransaction();
service.someMethod(...);
tx.commit();
}
}
it works too , but it is the same ugly too !
Now , my question is , is there any way for Spring to automatically open the related transactionManager(s) and begin/end tx ?
PS : I noticed this : 10.5.6.2 Multiple Transaction Managers with @Transactional , but it seems not fulfill my requirement : to open another txManager in ONE
method.
Environments : spring-3.0.5 , hibernate-3.6.0 , JPA2
-- updated --
Thanks @Bozho for telling me to call a new @Transactional(value="txMgrName") method , I tried , but still failed :
Here is my Service code :
@Override
@Transactional
public void someMethod(foo bar)
{
appDao.save(...); //success
someCoreMethod();
}
@Transactional(value="transactionManagerCore" , propagation=Propagation.REQUIRES_NEW)
private void someCoreMethod(...)
{
coreDao.save(...); //failed
}
in core.xml :
<bean id="transactionManagerCore" class="org.springfram开发者_如何转开发ework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactoryCore" />
<qualifier value="transactionManagerCore"/>
</bean>
It still failed , the coreDao still saves nothing. I think maybe it's because the method is private , and not intercepted by Spring . So I extract the method into interface/implementation level :
Service (interface)
public void someMethod(foo bar)
public void someCoreMethod(...)
ServiceImpl (class) : unchanged
But it still failed ! In fact , I found spring skips the @Transactional annotation in someCoreMethod().
I can even annotate @Transactional(value="non-existence-txManager-name") with a WRONG txManager , and Spring doesn't report any error (and commits nothing) !
Did I miss anything ?
You can do this via xml - <aop:config>
(there is an example in the docs you linked). It will create two proxies around the object, and hence the 2 transactions will be committed. It's a different story whether this is a best practice.
Another option is to invoke a new method (in a new class) that has
@Transactional(propagation=REQUIRES_NEW, "anotherTransactionManager")
精彩评论