我是不是需要在 grails 中显式刷新 GORM 保存调用?
Posted
技术标签:
【中文标题】我是不是需要在 grails 中显式刷新 GORM 保存调用?【英文标题】:Do I ever need to explicitly flush GORM save calls in grails?我是否需要在 grails 中显式刷新 GORM 保存调用? 【发布时间】:2011-09-11 10:51:02 【问题描述】:我遇到了一个奇怪的情况,似乎表明存在 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 语句都将返回所有书籍。
我的印象是在单个应用程序中没有必要这样做。
我正在使用的参考
数据库:mysql Groovy:1.7.10 圣杯:1.3.7@Hoàng Long
我的问题如下所示,假设 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
【问题讨论】:
【参考方案1】:我是否需要在 grails 中显式刷新 GORM 保存调用?
简而言之是的!,如果您想像在代码中那样立即使用该对象。
我遇到了同样的问题,所以这是我在阅读一些参考后得到的图片。
这是休眠会话问题。 Hibernate 会话在调用控制器操作时创建,并在操作返回时结束(或提前因错误而死)。如果代码没有调用任何事务代码,Hibernate 的数据库交互可以这样描述: 假设入口动作名称是 actionName 并且对动作的调用完成且没有任何错误。
NB:中间条(二级缓存被禁用)因为没有任何事务代码。
如果上面相同的代码有错误:
但是,如果您的操作正在调用事务方法或使用 withTransaction 创建内联事务(并假设对操作的调用已完成且没有任何错误)。
如果上面的代码有错误:
我希望它有所帮助,但如果我犯了任何错误或遗漏了重点,请评论我,我会更新我的照片。
【讨论】:
很好的解释! 这很棒,漂亮的图形!【参考方案2】:在您的情况下,第一条语句返回空列表,因为它从数据库中读取数据,但数据还没有。
Hibernate 是这样工作的:当您使用 (flush: true)
调用 save 时,它将刷新 Hibernate 会话,将会话中的所有数据立即持久化到数据库。如果不使用(flush:true)
,则数据仅记录在 Hibernate 会话中,并且仅在刷新 Hibernate 会话时保存在数据库中。刷新会话的时间由 Hibernate 自动确定以优化性能。
通常,您应该让 Hibernate 为您完成工作(为了优化) - 除非您希望数据立即持久化。
根据彼得·莱德布鲁克的说法:
让 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()
【讨论】:
很公平。我在问题中给出的示例是该问题的理想化案例。实际上,每次调用 save() 都不知道以后会使用什么查询......我不想总是出于恐惧而使用 (flush:true),但我还能做什么? @Akusete:在你的情况下,你能不能只在最后调用 hibernate 来刷新会话? 此链接可能有用:grails.org/… @Hoàng:我的问题是 save() 被调用了很多次......每次都无法知道它是否是最后一次。 在进行需要硬 db 调用的 db 查询时,有什么方法可以强制自动调用 flush 吗?【参考方案3】:我想知道您的 FlushMode 设置是什么。
默认情况下,它设置为“auto”,这意味着在每个直接访问 DB 的查询之前都会刷新会话(可能在其他情况下也是如此)。在这种情况下,您的 Foo.findAllByBar 应该首先刷新会话(可能是性能问题!)并从数据库中读取正确的值。
FlushMode 还有两个其他值,如果您设置其中之一,那么它会解释您的问题。 首先是“manual”,这意味着您决定手动刷新会话(例如,使用 save(flush:true))。如果您不这样做,那么 Foo.findAllByBar 会读取过时的数据库状态。 第二个是“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
【讨论】:
【参考方案4】:对于附加信息,您不能在域类事件(afterUpdate、beforeUpdate 等)中使用 flush 或 save(flush:true),这将导致堆栈溢出错误。 不过,您可以使用 save() 而不使用刷新。
gorm docs
【讨论】:
以上是关于我是不是需要在 grails 中显式刷新 GORM 保存调用?的主要内容,如果未能解决你的问题,请参考以下文章