5

mdb 内で例外を処理するにはどうすればよいですか? try catch ブロックの後に例外が発生するというおかしな感じがするので、それをキャッチしてログに記録できません。Glassfish v3 は、メッセージ全体を繰り返すことを決定します。無限ループに陥り、ハードドライブに大量のログファイルを書き込みます。

私はGlassfishv3.01 + Eclipselink 2.0.1を使用しています

public class SaveAdMessageDrivenBean implements MessageListener {

    @PersistenceContext(unitName="QIS") 
    private EntityManager em;

    @Resource
    private MessageDrivenContext mdc;

    public void onMessage(Message message) {
        try {
            if (message instanceof ObjectMessage) {
                ObjectMessage obj = (ObjectMessage)message;
                AnalyzerResult alyzres = (AnalyzerResult)obj.getObject();
                save(alyzres);
            }
        } catch (Throwable e) { 
            mdc.setRollbackOnly();
            log.log(Level.SEVERE, e);
        }
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    private void save(AnalyzerResult alyzres) throws PrdItemNotFoundException {

       Some s = em.find(Some.class, somepk);
       s.setSomeField("newvalue");

       // SQL Exception happens after leaving this method because of missing field for ex.
    }
}    
4

3 に答える 3

5

メッセージポイズニングの悪いケースがあります...

私が見る主な問題は次のとおりです。

  • でメソッドを直接呼び出していsave()ますonMessage()。これは、コンテナがメソッドの周りに適切なトランザクション処理プロキシを注入する方法がないことを意味saveします
  • いずれにせよ、save()メソッドは @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)別のトランザクションでコミットする必要があります。それ以外の場合は、onMessageトランザクションに参加し (デフォルトではREQUIRED)、例外処理コードをバイパスし、実行が成功した後にコミットされます。onMessage

私がすることは次のとおりです。

saveメソッドを新しいステートレス セッション Bean に移動します。

@Stateless
public class AnalyzerResultSaver
{
    @PersistenceContext(unitName="QIS") 
    private EntityManager em;

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    private void save(AnalyzerResult alyzres) throws PrdItemNotFoundException {
        Some s = em.find(Some.class, somepk);
        s.setSomeField("newvalue");
        // SQL Exception happens after leaving this method
    }
}

この Bean を MDB に注入します。

public class SaveAdMessageDrivenBean implements MessageListener {

    @Inject  
    private AnalyzerResultSaver saver;

    @Resource
    private MessageDrivenContext mdc;

    public void onMessage(Message message) {
        try {
            if (message instanceof ObjectMessage) {
                ObjectMessage obj = (ObjectMessage)message;
                AnalyzerResult alyzres = (AnalyzerResult)obj.getObject();
                saver.save(alyzres);
            }
        } catch (Throwable e) { 
            mdc.setRollbackOnly();
            log.log(Level.SEVERE, e);
        }
    }
}

もう 1 つのヒント: このコードでは、メッセージ ポイズニングがまだ存在しています。これは、呼び出し元の行から派生しますmdc.setRollbackOnly();

ここでは、例外をログに記録し、メッセージをポイズン キューに転送して、コンテナーがメッセージを無限に再送信するのを防ぐことをお勧めします。

アップデート:

「ポイズン キュー」または「エラー キュー」は、(できれば回復可能な) 破棄されたメッセージが完全に失われないことを保証するための単なる手段です。メッセージ データの正確性が保証されない統合シナリオで頻繁に使用されます。

ポイズン キューを設定することは、宛先キューまたはトピックを定義し、この宛先に「不良」メッセージを再配信することを意味します。

オペレーターは定期的にこのキューを (専用アプリケーションを介して) 検査し、メッセージを変更して「正常な」キューに再送信するか、メッセージを破棄して再送信を要求する必要があります。

于 2012-12-12T13:19:10.583 に答える
2

あなたが投稿したコードはほとんど問題ないと思います。

あなたの使用

    @TransactionAttribute(TransactionAttributeType.REQUIRED)

この (および他のほとんどの) アノテーションはビジネス メソッド (onMessage を含む) にのみ適用できるため、完全に無視されます。onMessage メソッドは暗黙的なメソッドを無料で取得するため、それは問題ではありません。

これは、Java EE コンテナではメッセージ処理がトランザクションであるという事実につながります。何らかの理由でトランザクションが失敗した場合、コンテナはメッセージの再配信を試行する必要があります。

これで、コードは save メソッドから例外をキャッチしています。これは良いことです。ただし、ロールバックのトランザクションを明示的にマークしています。これには、メッセージの配信が失敗したため、再試行する必要があることをコンテナーに伝える効果があります。

したがって、次を削除すると:

    mdc.setRollbackOnly();

コンテナはメッセージの再配信を停止します。

于 2012-12-12T13:37:57.630 に答える
1

私が間違っていなければ、コンテナにトランザクションを処理させています。このように、エンティティ マネージャーは、メソッドの終了後にフラッシュされる操作をキューに入れるため、メソッドの終了後に例外が発生します。

メソッドの最終ステップとして直接使用すると、トランザクションの関連するすべてのクエリが実行され、後でトランザクションのコミット中にコンテナーによって作成されたem.flush()ときに例外がスローされるのではなく、そこで例外がスローされます。flush()

于 2012-12-12T12:56:44.353 に答える