我是否需要在grails中明确刷新GORM保存调用?

时间:2011-06-09 06:40:17

标签: grails gorm object-persistence

我有一种奇怪的情况,似乎表明存在GORM缓存问题

//begin with all book.status's as UNREAD
Book.list().each { book.status = Status.READ ; book.save() }

println (Book.findAllByStatus (Status.READ)) //will print an empty list
println (Book.list().findAll (it.status == Status.READ)) // will print all books   

我无法理解为什么最后两个查询会返回不同的结果。

但是,如果我对 book.save(flush:true)进行以下修改。这两个println语句都将返回所有书籍。

我的印象是在单个应用程序中没有必要这样做。

供参考我正在使用

  • DB:mysql
  • Groovy:1.7.10
  • Grails:1.3.7

@HoàngLong

我的问题如下所示,假设action1 / action2被多次调用,没有特定的模式

def action1 = {
   Foo foo = Foo.get(params.id)
   //... modify foo 
   foo.save() //if I flush here, it will be inefficient if action1 is called in sequence
}

def action2 = {
   //if I flush here, it will be inefficient if action2 is called in sequence
   List<Foo> foos = Foo.findAllByBar (params.bar)
   //... do something with foos
}

一种解决方案是使用一个标志,该标志由action1设置,并在必要时由action2用于刷新。我的问题是,这是一个过于复杂的解决方案,由于数据库调用的复杂性增加,因此无法扩展。

boolean isFlushed = true

def action1 = {
   Foo foo = Foo.get(params.id)
   //... modify foo 
   foo.save() 
   isFlushed = false
}

def action2 = {
   if (!isFlushed) {
      //flush hibernate session here
   }
   List<Foo> foos = Foo.findAllByBar (params.bar)
   //... do something with foos
}

4 个答案:

答案 0 :(得分:49)

  

我是否需要在grails中明确刷新GORM保存调用?

简而言之!,如果您想在代码中立即使用该对象。

我遇到了同样的问题,所以这是我读完一些参考后得到的图片。

这是 hibernate session 问题 Hibernate会话在调用控制器操作时创建,在操作返回时结束(或者在错误提前死亡)。如果代码没有调用任何事务代码,Hibernate的db交互可以这样描述:
假设输入操作名称为 actionName ,并且对操作的调用完成且没有任何错误。

NB 中间栏(禁用二级缓存),因为没有任何交易代码。 without transaction without error

如果上述相同的代码有错误:

without transaction with error

但是,如果您的操作是调用事务方法或使用 withTransaction 创建内联事务(并假设对操作的调用已完成且没有任何错误)。 with transaction without error

如果上面的代码有错误: with transaction with error

我希望它有所帮助,但如果我犯了任何错误或错过了包含重点,请评论我,我会更新我的照片。

答案 1 :(得分:31)

在您的情况下,第一个语句返回空列表,因为它从数据库中读取数据,但数据尚未存在。

这是Hibernate的工作原理:当您使用(flush: true)调用save时,它将刷新Hibernate会话,将会话中的所有数据持久保存到数据库立即。如果不使用(flush:true),则数据仅记录在Hibernate会话中,并且仅在刷新Hibernate会话时持久保存在数据库。 Hibernate会自动确定刷新会话的时间,以优化性能。

一般来说,你应该让Hibernate为你工作(为了优化) - 除非你想要立即保存数据。

Peter Ledbrook说:

  

让Hibernate完成它的工作,并且只在您完成时手动刷新会话   必须,或至少只在最后   一批更新。你应该只   真的用,如果你没有看到   应该是数据库中的数据   那里。我知道这有点儿   多愁善感,但情况   当这种行动是必要的时候   关于数据库的实现和   其他因素。

来自GORM Gotchas - part 1

更新:要清楚在保存所有对象后如何刷新会话一次:

import org.hibernate.*

class SomeController {
  SessionFactory sessionFactory

  def save = {
    assert sessionFactory != null

    // loop and save your books here

    def hibSession = sessionFactory.getCurrentSession()
    assert hibSession != null
    hibSession.flush()
  }
}

答案 2 :(得分:8)

我想知道您的FlushMode设置是什么。

默认情况下,它设置为“ auto ”,这意味着会话在每次直接命中DB的查询之前刷新(在其他情况下也可能)。在这种情况下,您的 Foo.findAllByBar 应首先刷新会话(可能的性能问题!)并从数据库中读取正确的值。

FlushMode还有另外两个值,如果您设置其中一个,那么它可以解释您的问题。 首先是“ manual ”,这意味着您决定手动刷新会话(例如使用save(flush:true))。如果你不这样做,那么 Foo.findAllByBar 会读取过时的DB状态。 第二个是“ commit ”,这意味着每次事务提交都会刷新会话。如果你在grails中使用“ withTransaction ”语句,这非常方便。

资源: http://schneide.wordpress.com/2011/03/08/the-grails-performance-switch-flush-modecommit/ http://docs.jboss.org/hibernate/entitymanager/3.5/reference/en/html/objectstate.html#d0e1215

答案 3 :(得分:1)

对于添加的信息,您不能在域类事件中使用flush或save(flush:true)(afterUpdate,beforeUpdate,ect)它将导致堆栈溢出错误。 你可以使用save()而不使用flush。

gorm docs