开发者

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")
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜