Spring自动开启另一个transactionManager?

发布于 2024-10-07 02:46:10 字数 4049 浏览 0 评论 0原文

我有两个数据库,有两组弹簧配置: 下层是 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

对风讲故事 2024-10-14 02:46:10

您可以通过 xml - 来完成此操作(您链接的文档中有一个示例)。它将在该对象周围创建两个代理,因此将提交 2 个事务。这是否是最佳实践则是另一回事。

另一种选择是调用一个新方法(在新类中),该方法具有

@Transactional(propagation=REQUIRES_NEW, "anotherTransactionManager")

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")
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文