参考
6.6 プログラマチックトランザクション
Interface TransactionStatus
Serviceを使ってみる
概要
Grailsでトランザクションを利用したい場合、Serviceを使ってみるにのように、ServiceというGrailsの機能を利用することで実現できます。
実はServiceだけじゃなくて、プログラマチックトランザクションというSpringの仕組みを利用しても実現できます。
GORMが拡張されているので非常に簡単。
サンプル
例えば以下のようなドメインが有る場合。
class Hoge {
String name
static constraints = {
}
}
基礎
このドメインに対してトランザクションを走らせたい場合は、以下のようなコードになります。
def index3() {
Hoge.withTransaction { TransactionStatus status ->
// 例えばINSERT
def newHoge = new Hoge(name:"koji").save()
}
render "OK"
}
上記のコードは正常にINSERTされます。
単純に、ドメインのwithTransactionというメソッドに、やりたいことをクロージャとして渡してあげるだけです。
では実際にロールバックするようにしてみます。
ロールバックさせる
def index3() {
Hoge.withTransaction { TransactionStatus status ->
// 例えばINSERT
def newHoge = new Hoge(name:"koji").save()
status.setRollbackOnly()
}
render "OK"
}
status.setRollbackOnly()
が実行されると、そのトランザクション(withTransactionブロック内)のデータベースに対する変更はすべてロールバックされます。
トランザクションの範囲
1トランザクション == withTransactionブロックなので、withTransactionブロックがループなどで複数回実行されている場合は、そのたびにトランザクションが異なります。
例えば以下の場合。
10回withTransactionブロックが実行されます。
def index3() {
(1..10) .each {Integer index ->
Hoge.withTransaction { TransactionStatus status ->
def newHoge = new Hoge(name:"koji-${index}").save()
// INSERTした結果、IDが奇数の場合はロールバックする。
if (newHoge.id % 2 != 0) {
status.setRollbackOnly()
}
}
}
render "OK"
}
これを実行すると、データベースにINSERTした際に、IDが奇数の場合にロールバックされます。
実行結果は以下のようになります。
非常に簡単ですね。
別のドメインの更新でも有効
なお、今回HogeドメインのwithTransactionを使っていますが、このHoge.withTransactionブロックの中で別のドメインを利用することもできます。
例えば以下のドメインを用意して、同じようにHogeのIDが奇数だった場合にロールバックするようにしてみます。
class Piyo {
String name
static constraints = {
}
}
以下のようなコードを実行します。
def index3() {
(1..10) .each {Integer index ->
Hoge.withTransaction { TransactionStatus status ->
def newHoge = new Hoge(name:"Hoge-${index}").save()
// Hogeドメインとは関係ないドメインのINSERT
def newPiyo = new Piyo(name:"Piyo-${index}").save()
// Hogeが奇数ならロールバック
if (newHoge.id % 2 != 0) {
status.setRollbackOnly()
}
}
}
render "OK"
}
実行結果は以下のようになります。
前回の実行結果が残っているのでちょっと見づらいですが、Hogeに関しては通常通り奇数がロールバックされていて、ちゃんと関係ないPiyoドメインもロールバックされていることが分かります。
まとめ
複雑な処理などはやはりServiceとして実装したほうがいいと思いますが、run-scriptで実行されるバッチなどの簡単な処理などであれば非常に便利な機能だと思います。