26

次のようなステートレス Bean があります。

@Stateless
public class MyStatelessBean implements MyStatelessLocal, MyStatelessRemote {
    @PersistenceContext(unitName="myPC")
    private EntityManager mgr;

    @TransationAttribute(TransactionAttributeType.SUPPORTED)
    public void processObjects(List<Object> objs) {
        // this method just processes the data; no need for a transaction
        for(Object obj : objs) {
            this.process(obj);
        }
    }

    @TransationAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void process(Object obj) {
        // do some work with obj that must be in the scope of a transaction

        this.mgr.merge(obj);
        // ...
        this.mgr.merge(obj);
        // ...
        this.mgr.flush();
    }
}

通常、クライアントは processObjects(...) を呼び出しますが、これは実際にはエンティティ マネージャーと対話しません。必要なことを実行し、処理するオブジェクトごとに個別に process(...) を呼び出します。process(...) の期間は比較的短いですが、processObjects(...) はすべてを実行するのに非常に長い時間がかかる可能性があります。したがって、オープントランザクションを維持したくありません。独自のトランザクション内で動作するには、個々のプロセス (...) 操作が必要ですこれは、呼び出しごとに新しいトランザクションである必要があります。最後に、クライアントが process(...) を直接呼び出すためのオプションを開いたままにしておきたいと思います。

私はさまざまな種類のトランザクションを試しました: 決して、サポートされていない、サポートされている (processObjects 上で)、必須、新しい (プロセス上で) が必要ですが、merge() が呼び出されるたびに TransactionRequiredException が発生します。

メソッドを2つの異なるBeanに分割することで、機能させることができました。

@Stateless
@TransationAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class MyStatelessBean1 implements MyStatelessLocal1, MyStatelessRemote1 {
    @EJB
    private MyStatelessBean2 myBean2;

    public void processObjects(List<Object> objs) {
        // this method just processes the data; no need for a transaction
        for(Object obj : objs) {
            this.myBean2.process(obj);
        }
    }
}

@Stateless
public class MyStatelessBean2 implements MyStatelessLocal2, MyStatelessRemote2 {
    @PersistenceContext(unitName="myPC")
    private EntityManager mgr;

    @TransationAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void process(Object obj) {
        // do some work with obj that must be in the scope of a transaction

        this.mgr.merge(obj);
        // ...
        this.mgr.merge(obj);
        // ...
        this.mgr.flush();
    }
}

しかし、これを1つのクラスで達成できるかどうかはまだ興味があります. トランザクション マネージャーは、個々のメソッドにより具体的な注釈が与えられている場合でも、Bean レベルでのみ動作するように見えます。したがって、トランザクションが同じインスタンス内で他のメソッドの呼び出しを開始しないように 1 つのメソッドをマークすると、どのようにマークされていても、トランザクションは作成されませんか?

JBoss Application Server 4.2.1.GA を使用していますが、具体的ではない回答を歓迎/優先します。

4

8 に答える 8

24

それを行う別の方法は、実際には同じBeanに両方のメソッドを持ち、@EJBそれ自体への参照を持つことです。そんな感じ:

// supposing processObjects defined on MyStatelessRemote1 and process defined on MyStatelessLocal1
@Stateless
@TransationAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class MyStatelessBean1 implements MyStatelessLocal1, MyStatelessRemote1 {
    @EJB
    private MyStatelessLocal1 myBean2;

    public void processObjects(List<Object> objs) {
        // this method just processes the data; no need for a transaction
        for(Object obj : objs) {
            this.myBean2.process(obj);
        }
    }


    @TransationAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void process(Object obj) {
        // do some work with obj that must be in the scope of a transaction

        this.mgr.merge(obj);
        // ...
        this.mgr.merge(obj);
        // ...
        this.mgr.flush();
    }
}

このようにして、実際にprocess()は、プロキシのejbスタックを介してメソッドにアクセスするように「強制」するため@TransactionAttribute、有効になり、1つのクラスのみが保持されます。ふぅ!

于 2009-02-04T15:46:23.633 に答える
4

Matt さん、あなたの質問はかなり古典的なものです。Herval/Pascal による自己参照ソリューションはすばらしいと思います。ここで言及されていない、より一般的な解決策があります。

これは、EJB の「ユーザー」トランザクションの場合です。セッション Bean にいるため、セッション コンテキストからユーザー トランザクションを取得できます。コードがユーザー トランザクションでどのように表示されるかを次に示します。

// supposing processObjects defined on MyStatelessRemote1 and process defined on MyStatelessLocal1
@Stateless
@TransationAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class MyStatelessBean1 implements MyStatelessLocal1, MyStatelessRemote1 {

    @Resource
    private SessionContext ctx;

    @EJB
    private MyStatelessLocal1 myBean2;

    public void processObjects(List<Object> objs) {
        // this method just processes the data; no need for a transaction
        for(Object obj : objs) {
            this.myBean2.process(obj);
        }
    }


    public void process(Object obj) {

        UserTransaction tx = ctx.getUserTransaction();

        tx.begin();

        // do some work with obj that must be in the scope of a transaction

        this.mgr.merge(obj);
        // ...
        this.mgr.merge(obj);
        // ...
        this.mgr.flush();

        tx.commit();
    }
}
于 2010-04-17T22:06:24.760 に答える
2

問題は、各 Bean がトランザクション動作を制御するプロキシにラップされていることだと思います。ある Bean から別の Bean に呼び出す場合、その Bean のプロキシを経由することになり、プロキシによってトランザクションの動作を変更できます。

ただし、Bean が別のトランザクション属性でそれ自体のメソッドを呼び出す場合、その呼び出しはプロキシを経由しないため、動作は変わりません。

于 2008-12-04T16:32:11.030 に答える
1

マット、それが価値があることについて、私はあなたとまったく同じ結論に達しました。

TransactionAttributeTypesは、Beanの境界を越えるときにのみ考慮されます。同じBean内のメソッドを呼び出す場合、TransactionAttributeTypesは、メソッドにどのタイプが設定されていても効果がありません。

私が見る限り、EJB永続性仕様には、これらの状況での動作を指定するものはありません。

これはJbossでも経験しました。Glassfishでも試してみて、結果をお知らせします。

于 2008-12-04T16:15:50.130 に答える
1

誰かがこれに出くわした場合:

JBoss で循環依存関係を回避するには (自己参照を許可するなど)、たとえば次のようにアノテーション「IgnoreDependency」を使用します。

@IgnoreDependency @EJB MySelfselfRef;

于 2010-05-07T19:09:07.247 に答える
1

私はまだ試していませんが (しようとしています)、@EJB注釈を介して自己参照を挿入する代わりにSessionContext.getBusinessObject()メソッドを使用できます。これは、循環参照によって問題が発生する可能性を回避するもう 1 つの方法です。ただし、少なくともステートレス Bean インジェクションでは機能するようです。

私は両方の手法が採用されている大規模なシステムに取り組んでいますが (おそらく異なる開発者によって)、どちらが「正しい」方法であるかはわかりません。

于 2012-04-11T15:25:12.737 に答える
0

メソッドprocessObjectsの@TransationAttribute(TransactionAttributeType.Never)に関係していると思います。

TransactionAttributeType.Never

http://docs.sun.com/app/docs/doc/819-3669/6n5sg7cm3?a=view

クライアントがトランザクション内で実行されていて、エンタープライズ Bean のメソッドを呼び出す場合、コンテナは RemoteException をスローします。クライアントがトランザクションに関連付けられていない場合、コンテナはメソッドを実行する前に新しいトランザクションを開始しません。

あなたがクライアント コードからのメソッドprocessObjectsのクライアントであると仮定します。おそらく、クライアントはトランザクションに関連付けられていないため、TransactionAttributeType.Neverを使用したメソッド呼び出しは、そもそも満足しています。次に、 TransactionAttributeType.Requiredアノテーションを持つことはBean メソッド呼び出しではなく、トランザクション ポリシーは適用されませんが、 processObjectsからプロセスメソッドを呼び出します。mergeを呼び出すと、まだトランザクションに関連付けられていないため、例外が発生します。

両方の Bean メソッドにTransactionAttributeType.Requiredを使用して、うまくいくかどうかを確認してください。

于 2008-09-20T00:01:37.807 に答える
0

ケビンが言及したこれらの循環依存の問題がありました。ただし、提案されたアノテーション @IgnoreDependency は jboss 固有のアノテーションであり、Glassfish などに対応するものはありません。

デフォルトの EJB 参照では機能しないため、このソリューションには少し違和感を覚えました。

したがって、私はブルーカーボンのソリューションにチャンスを与え、内部トランザクションを「手動で」開始しました。

これに加えて、内部の process() を別の Bean に実装する以外に解決策はありません。これは、そのような技術的な詳細のためにクラス モデルを妨害したいだけなので、これも醜いです。

于 2011-09-26T14:31:46.483 に答える