Spring自动开启另一个transactionManager?
我有两个数据库,有两组弹簧配置: 下层是 CORE
db ,上层是 APP
db 。
每个数据库都有其 persistenceUnit 、entityManagerFactory 、 transactionManager ,并附加数据库名称, 例如“entityManagerFactoryApp”,“transactionManagerCore”...
现在,我有一个Service类,在APP中包装一些DAO,在CORE中包装一些DAO。但我发现我无法在测试中提交 CORE 的 DAO :
这是我的 Service 类:
@Inject private AppDao appDao;
@Inject private CoreDao coreDao;
@Override
@Transactional
public void someMethod(foo bar)
{
appDao.save(...); //success
coreDao.save(...); //failed !
}
这是我的测试类:
@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(...);
}
}
我知道我无法提交 CORE 的 DAO 的原因是因为测试类的 @TransactionConfiguration 是“transactionManagerApp" ,而不是 "
transactionManagerCore
" 。 因此,CORE 的 DAO 中的任何 CREATE/UPDATE/DELETE 操作都不会被提交。但我无法同时启用两个 txManager(有什么办法吗?)。
所以,我修改了我的服务类:
@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();
}
是的,它有效!但这不是我想要的!因为它引入了很多冗余代码(session、tx、commit...)。
而且...还有另一种方法,从 Service 中删除 session/EntityManagerFactoryUtils,并将它们移至测试类:
@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();
}
}
它也可以工作,但也同样丑陋!
现在,我的问题是,Spring有没有办法自动打开相关的transactionManager并开始/结束tx?
PS:我注意到这一点: 10.5.6.2 使用 @Transactional 的多个事务管理器,但它似乎不能满足我的要求:在 ONE 方法中打开另一个 txManager。
环境:spring-3.0.5,hibernate-3.6.0,JPA2
- 更新 -
感谢@Bozho告诉我调用一个新的@Transactional(value =“txMgrName”)方法,我尝试过,但仍然失败:
这里是我的服务代码:
@Override
@Transactional
public void someMethod(foo bar)
{
appDao.save(...); //success
someCoreMethod();
}
@Transactional(value="transactionManagerCore" , propagation=Propagation.REQUIRES_NEW)
private void someCoreMethod(...)
{
coreDao.save(...); //failed
}
在 core.xml 中:
<bean id="transactionManagerCore" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactoryCore" />
<qualifier value="transactionManagerCore"/>
</bean>
它仍然失败,coreDao 仍然没有保存任何内容。我想也许是因为该方法是 private 的,并且没有被 Spring 拦截。 所以我将该方法提取到接口/实现级别:
Service (interface)
public void someMethod(foo bar)
public void someCoreMethod(...)
ServiceImpl (class) : unchanged
但它仍然失败!事实上,我发现 spring 在 someCoreMethod() 中跳过了 @Transactional 注释。
我什至可以用错误的 txManager 注释 @Transactional(value="non-existence-txManager-name") ,并且 Spring 不会报告任何错误(并且不会提交任何内容)!
我错过了什么吗?
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.springframework.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 ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您可以通过 xml -
来完成此操作(您链接的文档中有一个示例)。它将在该对象周围创建两个代理,因此将提交 2 个事务。这是否是最佳实践则是另一回事。另一种选择是调用一个新方法(在新类中),该方法具有
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