2

我有两个数据库,有两组弹簧配置:下层是COREdb,上层是APPdb。

每个 db 都有它的 persistenceUnit 、 entityManagerFactory 、 transactionManager ,并附加了 db 名称,例如 "entityManagerFactoryApp" 、 "transactionManagerCore" ...

现在,我有一个 Service 类,在 APP 中包装了一些 DAO,在 CORE 中包装了一些。但我发现我无法在测试中提交 CORE 的 DAO:

这是我的服务类:

  @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...)。

而且......还有另一种方法,从服务中删除会话/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(s) 并开始/结束 tx?

PS:我注意到这一点:10.5.6.2 Multiple Transaction Managers with @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 仍然没有保存任何内容。我想可能是因为该方法是私有的,而不是被 Spring 拦截。所以我将方法提取到接口/实现级别:

Service (interface)
  public void someMethod(foo bar)
  public void someCoreMethod(...)

ServiceImpl (class) : unchanged

但是还是失败了!事实上,我发现spring跳过了someCoreMethod()中的@Transactional注解

我什至可以用 WRONG txManager 注释 @Transactional(value=" non-existence-txManager-name ") ,并且 Spring 不会报告任何错误(并且什么也不提交)!

我错过了什么吗?

4

1 回答 1

2

您可以通过 xml 执行此操作 - <aop:config>(您链接的文档中有一个示例)。它将围绕对象创建两个代理,因此将提交 2 个事务。这是否是最佳实践是另一回事。

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

@Transactional(propagation=REQUIRES_NEW, "anotherTransactionManager")
于 2010-12-12T17:37:46.267 回答