2

2つのデータベースがあり、2セットのスプリング構成があります。下位レベルはCOREdb、上位レベルはAPPdbです。

各dbには、persistenceUnit、entityManagerFactory、transactionManagerがあり、「entityManagerFactoryApp」、「transactionManagerCore」などのdb名が追加されています。

今、私はServiceクラスを持っており、いくつかのDAOをAPPでラップし、いくつかを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アクションはコミットされません。しかし、2つの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を自動的に開き、txを開始/終了する方法はありますか?

PS:私はこれに気づきました:10.5.6.2 @Transactionalを持つ複数のトランザクションマネージャー、しかしそれは私の要件を満たしていないようです:ONEメソッドで別のtxManagerを開きます。

環境:春-3.0.5、休止状態-3.6.0、JPA2

- 更新しました -

新しい@Transactional(value = "txMgrName")メソッドを呼び出すように指示してくれた@Bozhoに感謝します。試しましたが、失敗しました。

これが私のサービスコードです:

  @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アノテーションをスキップしていることがわかりました。

@Transactional(value = " non-existence-txManager-name ")に間違ったtxManagerで注釈を付けることもでき、Springはエラーを報告しません(そして何もコミットしません)!

私は何かを逃しましたか?

4

1 に答える 1

2

これはxmlを介して行うことができます- <aop:config>(リンクしたドキュメントに例があります)。オブジェクトの周りに 2 つのプロキシが作成されるため、2 つのトランザクションがコミットされます。これがベストプラクティスかどうかは別の話です。

別のオプションは、(新しいクラスで) 新しいメソッドを呼び出すことです。

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