playframework1‎ > ‎jpa‎ > ‎

トランザクションの制御

基本的にフレームワークがトランザクションを管理している。

httpリクエスト毎にトランザクションを開始し、レスポンスを返すときにコミットされる。
例外で終わったときには自動的にロールバックされる。

play1.3で、複数DB接続をサポートした影響で、トランザクション制御に影響が出ている。
分かる範囲でそれぞれ追記しておく。

アノテーション

DBの更新を行わないとき、以下のアノテーションを該当のメソッドに設定することで読み出し専用にすることができる。
@play.db.jpa.Transactional(readOnly=true)

トランザクションの自動開始をさせたくないときは、以下のアノテーションを該当メソッド、もしくは該当コントローラクラスに設定する。
@play.db.jpa.NoTransaction
DBアクセスが不要な場合、積極的に利用すると速度アップにつながる。

トランザクションの開始

JPAPlugin.startTx(false);
readOnlyなトランザクションの時は、引数にtrueを指定する。
既に開始しているトランザクションは破棄される。

play1.3以降

play1.3では、JPAPlugin.startTx は非推奨メソッドになっており、そのまま使うと、デフォルトのDBに対しての操作となる。

今後は下記メソッドにて、DB識別名を指定して操作する。
play.db.jpa.JPA.startTx(String, boolean)

デフォルトのDBであれば、JPAクラスに定義されている文字列を使って下記のように記述すればよい。
JPA.startTx(JPA.DEFAULT, false);


コミット

JPAPlugin.closeTx(false);
引数falseがコミットを意味する。trueならロールバックする。

play1.3以降

トランザクションの開始と同じく、DB識別名を指定して操作する。
また、コミット or ロールバックの指定を行なう引数は廃止となっている。
JPA.closeTx(JPA.DEFAULT);


ロールバック

JPA.setRollbackOnly();

JPAPlugin.closeTx(true);
どちらでも可能。
startTxで始まったら、closeTxを使って終わるのがスマートな気もする。

play1.3以降

DB識別名を指定して操作する。
JPA.rollbackTx(JPA.DEFAULT);



並列トランザクションを扱う場合

playframework1としては、1リクエスト1トランザクションを基本としている。
そのトランザクションはカレントのトランザクションとして、JPA.local.get(), set() でアクセスすることができる。
リクエスト処理中に例外が発生したときは、このトランザクションに対してロールバックが行われる。

幾つかのトランザクションを並列に扱うときは、カレントのトランザクションを切り替えることで実現することになる。
切替の際は、カレントのトランザクションを自前で、退避/復帰する。

新たなトランザクションはstartTxを用いて開始する。

※play1.3.0では、複数DB利用をサポートした影響で、JPA.localプロパティが無くなってしまった。そのため、ここに書かれている方法は使えない。

退避

カレントのトランザクションをtempContextに退避する。カレントトランザクションは無効にしておく。
JPA tran1 = JPA.local.get();
JPA.local.remove();

復帰

退避していたトランザクションを、カレントトランザクションに設定する。
JPA.local.set(tran1);

注意点

httpリクエスト処理内で例外が発生すると、playframeworkはカレントのトランザクションに対してロールバックを行う。
単一のトランザクションの場合は問題ないが、複数のトランザクションを扱っているときは、カレント以外のトランザクションのロールバックも忘れずに行う必要がある。

play1.3以降での並列トランザクション

今までは、Threadlocalで唯一のJPAコンテキストを保持する実装だった。
1.3.0では複数DB接続を実現するため、ThreadlocalでDB名とJPAコンテキストのMAPオブジェクトを保持する実装になった。

退避と復帰は、DBごとに行なう。

退避

// コンテキスト退避
JPAContext defContext = JPA.currentEntityManager.get().get(JPA.DEFAULT);
JPA.currentEntityManager.get().remove(JPA.DEFAULT);

// 新たなトランザクションを開始
JPA.startTx(JPA.DEFAULT, false);

復帰

// トランザクションを閉じる
JPA.closeTx(JPA.DEFAULT);

// コンテキスト復帰
if (defContext != null) {
    JPA.currentEntityManager.get().put(JPA.DEFAULT, defContext);
}
※退避コンテキストがNULLのときは復帰しなくていい。

注意

複数トランザクションを使い分ける時は、途中で例外が発生するなどして、トランザクション閉じ忘れが発生しないように注意が必要。
@Finally などで必ずトランザクションがすべて閉じられるように実装すべき。






参考: