2012-01-04 2 views
7

JBoss 5.1上で実行されるJavaアプリケーションがあります。場合によっては、JDBCExceptionがいくつかの基本メソッドによってスローされる場合にトランザクションがクローズされないようにする必要があります。JBoss + Hibernateでのトランザクションロールバックの防止

は、我々は、次の1 insertは(制約違反によって引き起こさJDBCExceptionをラップする)ためPersistenceExceptionで失敗した場合

@PersistenceContext(unitName = "bar") 
public EntityManager em; 

public Object foo() { 
    try { 
    insert(stuff); 
    return stuff; 
    } (catch PersistenceException p) { 
    Object t = load(id); 
    if (t != null) { 
     find(t); 
     return t; 
    } 
    } 
} 

、我々は内loadで実行を続けたいように見えるEJBメソッドを持っています同じ取引。

取引がコンテナによって閉鎖されているため、現時点ではこれを行うことはできません。ここでは、ログに見るものです:

org.hibernate.exception.GenericJDBCException: Cannot open connection 
javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: Cannot open connection 
    at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:614) 

    ... 

Caused by: javax.resource.ResourceException: Transaction is not active: tx=TransactionImple < ac, BasicAction: 7f000101:85fe:4f04679d:182 status: ActionStatus.ABORT_ONLY > 

EJBクラスには、次の注釈

@Stateless 
@TransactionManagement(TransactionManagementType.CONTAINER) 
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 

でマークされて戻ってちょうどこの特定のケースでは、圧延からトランザクションを防ぐために、任意の適切な方法はありますか?

答えて

5

本当にやろうとしないでください。別の答えやHibernate Docsの引用で言及されているように、Hibernateによってスローされた例外は回復可能であるとみなされません。これにより、特にhibernateの自動ダーティチェックを使用して、いくつかの難しい問題を発見/デバッグすることができます。

この問題を解決する明確な方法は、オブジェクトを挿入する前にそれらの制約をチェックすることです。照会を使用して、データベース制約が違反していないかどうかを検査します。

public Object foo() { 
    if (!objectExists()) { 
     insertStuff(); 
     return stuff(); 
    } 
    // Code for loading object... 
} 

私は、これは少し痛いようだけど、それはあなたが(あなたはHibernateの例外からその情報を取得することはできません)に違反した制約確実に知っているよ唯一の方法です。私はこれが最も安全なソリューションだと信じています(少なくとも安全です)。


例外から復旧したい場合は、コードをいくつか変更する必要があります。

前述のとおり、トランザクションは手動で管理できますが、推奨しません。 JTA APIは本当に面倒です。また、Bean Managed Transaction(BMT)を使用する場合は、EJB内のすべてのメソッドのトランザクションを手動で作成する必要があります。

一方、コンテナがクエリに対して異なるトランザクションを使用するように、メソッドをリファクタリングすることができます。このような何か:あなたがfoo()を呼び出すと

@Stateless 
public class Foo { 
    ... 
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
    public Object foo() { 
     try { 
      entityManager.insert(stuff); 
      return stuff; 
     } catch (PersistenceException e) { 
      if (e.getCause() instanceof ConstraintViolationException) { 
       // At this point the transaction has been rolled-backed. 
       // Return null or use some other way to indicate a constrain 
       // violation 
       return null; 
      } 
      throw e; 
     } 
    } 

    // Method extracted from foo() for loading the object. 
    public Object load() { 
     ... 
    } 
} 

// On another EJB 
@EJB 
private Foo fooBean; 

public Object doSomething() { 
    Object foo = fooBean.insert(); 
    if (foo == null) { 
     return fooBean.load(); 
    } 

    return foo; 
} 

、現在のトランザクション(T1)が中断され、コンテナは新しいもの(T2)を作成します。エラーが発生すると、T2はロールバックされ、T1が復元されます。 load()が呼び出されると、T1(まだアクティブ)が使用されます。

希望すると便利です。

2

私はそれが可能ではないと思います。

Inは、JPAプロバイダによって異なる場合がありますが、たとえば、Hibernateはセッションが不整合な状態になることを明示的に示しているため、回復可能(13.2.3. Exception handling)として扱うべきではありません。

私はこのメソッドの自動トランザクション管理を無効にし、手動で例外の後で新しいトランザクションを作成することをお勧めします(UserTransaction、覚えている限り)。

関連する問題