LoginSignup
3
3

More than 5 years have passed since last update.

[Grails]プログラマチックトランザクション

Last updated at Posted at 2014-05-08

参考

6.6 プログラマチックトランザクション
Interface TransactionStatus
Serviceを使ってみる

概要

Grailsでトランザクションを利用したい場合、Serviceを使ってみるにのように、ServiceというGrailsの機能を利用することで実現できます。

実はServiceだけじゃなくて、プログラマチックトランザクションというSpringの仕組みを利用しても実現できます。
GORMが拡張されているので非常に簡単。

サンプル

例えば以下のようなドメインが有る場合。

Hoge.groovy
class Hoge {

    String name
    static constraints = {
    }
}

基礎

このドメインに対してトランザクションを走らせたい場合は、以下のようなコードになります。

HelloController
def index3() {
    Hoge.withTransaction { TransactionStatus status ->
        // 例えばINSERT
        def newHoge = new Hoge(name:"koji").save()
    }
    render "OK"
}

上記のコードは正常にINSERTされます。
単純に、ドメインのwithTransactionというメソッドに、やりたいことをクロージャとして渡してあげるだけです。
では実際にロールバックするようにしてみます。

ロールバックさせる

HelloController
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ブロックが実行されます。

HelloController
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が奇数の場合にロールバックされます。
実行結果は以下のようになります。

console1.png

非常に簡単ですね。

別のドメインの更新でも有効

なお、今回HogeドメインのwithTransactionを使っていますが、このHoge.withTransactionブロックの中で別のドメインを利用することもできます。
例えば以下のドメインを用意して、同じようにHogeのIDが奇数だった場合にロールバックするようにしてみます。

Piyo.groovy
class Piyo {

    String name
    static constraints = {
    }
}

以下のようなコードを実行します。

HelloController.groovy
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"
}

実行結果は以下のようになります。

console2.png

前回の実行結果が残っているのでちょっと見づらいですが、Hogeに関しては通常通り奇数がロールバックされていて、ちゃんと関係ないPiyoドメインもロールバックされていることが分かります。

まとめ

複雑な処理などはやはりServiceとして実装したほうがいいと思いますが、run-scriptで実行されるバッチなどの簡単な処理などであれば非常に便利な機能だと思います。

3
3
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3