5

CMT (コンテナー管理トランザクション) で CDI を使用して、Web アプリのデータベースに接続し、フロントエンドから呼び出されたメソッドにマークを付けます。

@Transactional(value=TxType.REQUIRES_NEW)

これにより、新しい CDI トランザクションが作成されますが、このコード ブロックまたはこのメソッドから呼び出された他のコード ブロックを実行中に例外が発生した場合、エラー メッセージがスローされます。

javax.transaction.TransactionalException: Managed bean with Transactional annotation and TxType of REQUIRES_NEW encountered exception during commit javax.transaction.RollbackException: Transaction marked for rollback.
...
Caused by: javax.transaction.TransactionalException: Managed bean with Transactional annotation and TxType of REQUIRES_NEW encountered exception during commit javax.transaction.RollbackException: Transaction marked for rollback.
...
Caused by: javax.transaction.RollbackException: Transaction marked for rollback.

ロールバックの本当の原因が何であったかを簡単にデバッグできるように、ネストされたエラーを再スローするように CDI を取得する方法はありますか?

(Java-EE7、Glassfish 4.0、JSF 2.2.2 で実行)

4

2 に答える 2

8

これを行う最も簡単な方法は、CDI インターセプターを使用して例外をキャッチすることです。CDI インターセプターは次のように定義できます。

@InterceptorBinding
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface TransactionDebugger {
}

CDI Interceptor を定義したら、Interceptor アノテーションが使用されるときに実行されるクラスを作成する必要があります。@AroundInvoke を定義して、注釈を付けるメソッド内のコードの前にコードが呼び出されるようにします。 invocationContext.proceed()アノテーションを付けたメソッドを呼び出し、返された結果 (存在する場合) を返します。したがって、try, catch (Exception)スローされたあらゆる種類の例外をキャッチするために、この呼び出しを囲むことができます。次に、ロガー (ここでは log4j を使用) を使用してこの例外をログに記録し、上流のコードにも通知されるように例外を再スローします。

例外を再スローすると、CMT (コンテナー管理トランザクション) を使用することもできます。最終的にコンテナーは例外をキャッチし、トランザクション RollbackException をスローします。ただし、これで UserTransactions を簡単に使用して、例外がキャッチされたときに再スローする代わりに手動でロールバックを実行することもできます。

@Interceptor
@TransactionDebugger
public class TransactionInterceptor {
    private Logger logger = LogManager.getLogger();

    @AroundInvoke
    public Object runInTransaction(InvocationContext invocationContext) throws Exception {
        Object result = null;
        try {
            result = invocationContext.proceed();
        } catch (Exception e) {
            logger.error("Error encountered during Transaction.", e);
            throw e;
        }
        return result;
    }
}

CDI では Interceptor がデフォルトで有効になっていないため、次に、beans.xml (通常は src/META-INF にあります) に新しい Interceptor を含める必要があります。これは、注釈が定義されているプロジェクトだけでなく、注釈が使用されているすべてのプロジェクトで行う必要があります。これは、CDI がプロジェクトごとにインターセプターを初期化するためです。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
                           http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       version="1.1" bean-discovery-mode="all">
    <interceptors>
        <class>package.database.TransactionInterceptor</class>
    </interceptors>
</beans>

最後に、新しい CDI Interceptor で呼び出すメソッドに注釈を付ける必要があります。ここでは、トランザクションを開始するために @Transactional でアノテーションを付け、トランザクション内で発生する例外をキャッチするために @TransactionDebugger でアノテーションを付けます。

@Transactional @TransactionDebugger
public void init() {
    ...
}

これにより、init() コードの実行中に発生したすべてのエラーがログに記録されるようになりました。ロギングの粒度は、try、catch を Exception から Interceptor 実装クラス TransactionInterceptor の Exception のサブクラスに変更することで変更できます。

于 2013-11-22T08:17:23.617 に答える