症状
Gant Scripts 中で任意のサービスメソッドを呼び出した際に、session が flushされない
(変更が保存されない)
grails-app/services/UserService.groovy
def updateUser() {
User user = User.findById(1L)
user.doSomething()
user.save()
}
scripts/Test.groovy
// サービスメソッドをcall
def service = appCtx.getBean("userService")
service.updateUser()
解 1: 'flush: true'
UserService.groovy
を書き換えるという、元も子もない対策。
これで済むなら問題ない。
grails-app/services/UserService.groovy
def updateUser() {
User user = User.findById(1L)
user.doSomething()
user.save(flush: true)
}
解 2: Transactionalアノテーションを利用する
- サービスに
static transactional = false
を設定し、NonTransactional な Bean にする。 - メソッドを抜けるときにflushするよう、Transactional アノテーションを付加する
grails-app/services/UserService.groovy
@Transactional
def updateUser() {
User user = User.findById(1L)
user.doSomething()
user.save()
}
補足
static transactional = false
を設定しないまま、
grails-app/services/UserService.groovy
@Transactional(propagation=Propagation.REQUIRES_NEW)
def updateUser() {
User user = User.findById(1L)
user.doSomething()
user.save()
}
も実験したが、動作せず
解 3: 明示的flush (recommended)
Grails に同梱されている scripts/RunScript.groovy
を参考に、スクリプトを以下のように修正する
Test.groovy
def persistenceInterceptor = appCtx.containsBean('persistenceInterceptor') ? appCtx.persistenceInterceptor : null
persistenceInterceptor?.init()
try {
// サービスメソッドをcall
def service = appCtx.getBean("userService")
service.updateUser()
}
finally {
persistenceInterceptor?.flush()
persistenceInterceptor?.destroy()
}
解1,2と比べると一見冗長で汚く見えるが、コードがいくらスッキリしたところでサービス側に手を加える事自体が本来的でない。更に言えば Spring のアノテーションは記述量こそ少ないが、挙動は別途調べ、覚えておく必要がある。他のプログラマが見た時の学習コストも鑑みると、こういった Grails 的なコードの方が見通しが良く合理的だと思う。