8

編集皆さんの回答に感謝しますが、問題は私のデータソース構成にあり、実際には自動コミットモードでした。詳細については、以下の私の回答を参照してください。

EntityManager.flush()メソッドのJavadocとGoogleでの検索はどちらも、flushメソッドが保留中のステートメントのみをデータベースに送信し、トランザクションをコミットしないことを示唆しているようです。しかし、私が作成した単純なテストWebサービス(Java 7、Oracle 11gR2、JBoss 7.1、およびWebサービスはjarファイルとしてパッケージ化されています)は、そうでないことを示しているようです。

これはテーブル作成スクリプトです。

CREATE TABLE test(
    id INTEGER NOT NULL,
    name VARCHAR2(20), 
    CONSTRAINT test_pk PRIMARY KEY ("ID")
);
CREATE SEQUENCE test_seq;

これは対応するエンティティです。

@Entity @Table(name = "TEST")
public class Test implements Serializable {

    private static final long serialVersionUID = 9192814682033048425L;

    @Id @Column(name = "ID")
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TEST_SEQ")
    @SequenceGenerator(name="TEST_SEQ",sequenceName="TEST_SEQ", allocationSize = 1)
    private Integer id;

    @Column(name = "NAME")
    private String name;

    // Getters and setters...
}

そして、テストWebサービス:

@Stateless @WebService(serviceName = "TestService")
@TransactionManagement(TransactionManagementType.CONTAINER)
public class TestServiceBean implements TestService {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public void createTest(String name) {
        Test test = new Test();
        test.setName(name);
        entityManager.persist(test);
        entityManager.flush();

        throw new RuntimeException();
    }
}

私の理解はそれです:

  • メソッドが呼び出されると、createTestアプリケーションは新しいトランザクションを開始します
  • このpersist()メソッドは、データベースに送信されるINSERTステートメントを生成します
  • このflush()メソッドはINSERTステートメントをデータベースに送信しますが、トランザクションをコミットしません。
  • RuntimeExceptionにより、トランザクションがロールバックされます。

しかし、明らかに私の理解は間違っています。Webサービスメソッドを実行するたびに、テーブルに1つの新しい行が表示されます。さらに、デバッガーを使用してこのメ​​ソッドにステップインすると、flush()メソッドが呼び出されたときに行が挿入されていることがわかります(SQL Developerを使用して別のデータベースセッションから行を「見る」ことができます)。

誰かがこの振る舞いを説明できますか?

4

3 に答える 3

8

結局、何も悪いことはないようですflush()。問題は、JBossでデータソースを正しく設定しなかったことです。ここでの教訓は、EBJでコンテナ管理トランザクションを使用する場合は、次のことを行う必要があるということです。

  • JBossで、Use JTA?をチェックします。データソース構成のチェックボックス。
  • Weblogicで、データソース構成の[トランザクション]タブにある[グローバルトランザクションをサポートする]チェックボックスをオンにします。

さらに、混乱を解消するために、私のコードのトランザクション管理は正しいです。をスローすると、例外RuntimeException がロールバックされます。何故ですか?さて、Java EE6チュートリアルから私たちは持っています:

システム例外がスローされた場合、コンテナは自動的にトランザクションをロールバックします。

しかし、システム例外とは何ですか?チュートリアルではこれ以上主題に触れていないようですので、EJB仕様を検索してみましょう。382ページには次のようなものがあります。

システム例外は、java.rmi.RemoteException(またはそのサブクラスの1つ)である例外、またはアプリケーション例外ではないRuntimeExceptionです。

では、RuntimeExceptionはアプリケーションの例外なのかもしれません。いいえ、そうではありません。380ページに次のようなものがあるからです。

チェックされた例外であるアプリケーション例外は、Beanのビジネス・インターフェース、インターフェースなしビュー、ホーム・インターフェース、コンポーネント・インターフェース、およびWebサービス・エンドポイントのメソッドのthrows節にリストされることによってそのように定義できます。チェックされていない例外であるアプリケーション例外は、ApplicationExceptionメタデータ注釈で注釈を付けるか、デプロイメント記述子でapplication-exception要素で注釈を付けることにより、アプリケーション例外として定義されます。

したがって、上記のいずれも実行しなかったため、コードでスローする例外は実際にはシステム例外であり、JTAを使用するようにデータソースを設定している場合は実際にトランザクションをロールバックします。

于 2012-11-18T17:22:44.537 に答える
0

JPA仕様の23ページのJSR-317を引用します。

プロパティアクセサメソッドによってスローされたランタイム例外により、現在のトランザクションにロールバックのマークが付けられます。永続性ランタイムが永続状態をロードまたは保存するために使用するときにこのようなメソッドによってスローされる例外により、永続性ランタイムは現在のトランザクションにロールバックのマークを付け、アプリケーション例外をラップするPersistenceExceptionをスローします。

したがって、スローするRuntimeExceptionは、EJBからではなくエンティティBeanセッターからスローする必要があります。

ロールバックのマークを示すラッピングPersistenceExceptionを取得しなかったと確信しています。代わりに、スローしたRuntimeExceptionを取得しました。

EJBからRuntimeExceptionの代わりにRollbackExceptionをスローしてみてください!!!

または、仕様にあるように、エンティティBeanセッター内からRuntimeExceptionをスローします。

于 2012-11-19T05:48:16.140 に答える
0

EJBメソッドでトランザクションをロールバックするには、メソッドを呼び出す必要がありますsetRollbackOnly()。そうしないと、例外をスローしてもメソッドを終了すると、トランザクションがコミットされます。詳細な説明については、JavaEE6チュートリアルを参照してください。

于 2012-11-18T15:12:54.330 に答える