【问题标题】:flush mode changed in grails from AUTO to MANUAL刷新模式在 grails 中从 AUTO 更改为 MANUAL
【发布时间】:2014-08-10 03:18:56
【问题描述】:

在将我的 Grails 项目从 1.3.7 升级到 2.4.0 并修复与新 grails 版本相关的各种问题之后,我意识到对任何对象所做的任何更改都不会再持久化(根本),除非@ 987654322@ 被调用。

在 Grails 1.3.7 中,使用 save() 保存域实例时的默认行为是,由于 hibernate flushMode => FlushMode.AUTO,更改会自动持久化。在 Grails 2.4.0 中,这不再是真的了。任何控制器操作或服务类中休眠会话的默认 flushMode 是FlushMode.MANUAL

在 BootStrap 中检索 sessionFactory.currentSession.flushMode 时,情况变得更加奇怪,它的值为 FlushMode.AUTO,而在控制器操作中它的值为 FlushMode.MANUAL。这可以通过创建一个新的 grails 应用程序并将println "flushMode = $sessionFactory.currentSession.flushMode" 放入 BootStrap 和控制器操作(例如 index())来验证。

在过去的 2 天里,我一直在搜索各种论坛,但没有找到任何合理的解释为什么必须在 Grails 2.4.0(或者甚至可能在早期版本中)进行更改。我只发现 cmets 说拥有 FlushMode.MANUAL 有点风险,因为在修改了其他一些数据库后,您可能会在查询数据库时遇到过时的对象。

我知道:

  • 在配置中使用grails.gorm.autoFlush = true,您可以对每个save() 强制执行flush:true
  • 在hibernate3和hibernate4中默认flushMode是FlushMode.AUTO
  • 无法在 Config.groovy 和 DataSource.groovy 中设置 flushMode。我尝试了所有这些,但没有任何效果: hibernate.flush.mode = 'auto' hibernate.flushMode = 'auto' hibernate.session.flush.mode = 'auto' hibernate.session.flushMode = 'auto' dataSource.hibernate.flush.mode = 'auto' dataSource.hibernate.flushMode = 'auto' dataSource.hibernate.session.flush.mode = 'auto' dataSource.hibernate.session.flushMode = 'auto' dataSource.flush.mode = 'auto' dataSource.flushMode = 'auto' dataSource.session.flush.mode = 'auto' dataSource.session.flushMode = 'auto'

请有人对此有所了解吗?

实际上我想知道在 Grails 2.4.0 中 FlushMode.MANUAL 现在是否是所需的默认值?

如果是这样:

  • Peter Ledbrook 在GRAILS-7180 中的评论“......建议不是我们完全禁用自动刷新模式......”是什么意思
  • 什么是最佳实践以避免遇到陈旧对象的问题,尤其是在对域对象进行复杂操作时,修改、创建新实例和查询都是混合的。

非常感谢 - 安迪


在阅读 Graemes Answer 和他的 cmets 后,我试图更好地阐明我正在努力解决的问题,并添加了以下简化的域和控制器类来证明这种行为:

域类消息:

class Msg {

    String  text

    static constraints = {
        text nullable:true
    }
}

和味精控制器:

class MsgController {
    def sessionFactory

    def index = {
        def out = ["*** flushMode when in controller/index = \
                   $sessionFactory.currentSession.flushMode"]
        Msg.list().each { out << "$it.id: text=$it.text" }
        render out.join('<br>')
    }

    // this save does persist the new msg object, 
    // even if flushMode = MANUAL
    def save1 = {
        def out = ["*** flushMode when in controller/save = \
                   $sessionFactory.currentSession.flushMode"]
        def msg = new Msg(text:'hallo')
        if (!msg.save()) {
            out << "msg has errors! " + msg.errors
        }
        out << "msg $msg.id created with text = $msg.text"
        render out.join('<br>')
    }

    // this save does NOT persist the new msg object, even if its valid
    // (difference is calling hasErrors()
    def save2 = {
        def out = ["*** flushMode when in controller/save = \
                   $sessionFactory.currentSession.flushMode"]
        def msg = new Msg(text:'hallo')
        if (msg.hasErrors() && !msg.save()) {
            out << "msg has errors! " + msg.errors
        }
        out << "msg $msg.id created with text = $msg.text"
        render out.join('<br>')
    }
}


所以调用http://localhost/appname/msg/save1 的输出是:

*** flushMode when in controller/save1 = MANUAL
msg 1 created with text = hallo

这里我不明白,为什么 hibernate 持久化对象,即使你 flushMode 是 MANUAL。

当调用http://localhost/appname/msg/save2 时,输出为:

*** flushMode when in controller/save2 = MANUAL
msg null created with text = hallo

对象没有被持久化,因为休眠没有发出刷新,因此从不调用 sql "update ..." 命令。

但现在看来,不仅flushMode 是一个问题,而且是否调用hasErrors() 也是一个问题!我更疑惑了……

如果您在 Grails 1.3.7 中执行此示例,则两个保存操作(save1 和 save2)都会保留新创建的 msg 对象!

【问题讨论】:

    标签: hibernate grails save flush


    【解决方案1】:

    Grails 将在验证之前将刷新模式设置为手动,以防止在验证期间刷新任何更改(这可能很常见,因为您可能有一个查询现有数据的自定义验证器)。

    https://github.com/grails/grails-data-mapping/blob/master/grails-datastore-gorm-hibernate4/src/main/groovy/org/codehaus/groovy/grails/orm/hibernate/validation/HibernateDomainClassValidator.java#L60

    如果有任何验证错误,它不会将刷新模式设置回 AUTO。这是为了防止无效对象被持久化。

    您所看到的是您可能发生了验证错误,尽管您可以强制刷新,但这是不可取的。

    【讨论】:

    • 感谢您的解释。那是有道理的。但是为什么即使没有保存(完全使用)域对象,Grails 也会在控制器操作中切换到 MANUAL 呢?我编写了一个只有 1 个控制器且没有域类的小型 Grails 应用程序来验证这一点。在控制器索引操作中,我只放了 1 个 LOC println "flushMode = $sessionFactory.currentSession.flushMode"
    • 对于读操作,Grails 使用只读事务。只读事务使用手动刷新模式。读操作使用只读事务的原因是因为它提高了性能,因为 Hibernate 不需要脏检查只读对象。这可能就是您所看到的。
    • 查看带有更新域和控制器示例的原始问题
    • 您的第二个示例没有意义,因为执行 if (msg.hasErrors() &amp;&amp; !msg.save()) 意味着永远不会调用 save(),因为 hasErrors() 将返回 false
    • 对不起,我的错字!它应该是if (msg.hasErrors() || !msg.save()),然后这两个操作都会保留 msg 对象,上面的整个示例毫无意义!我将不得不检查有问题的代码并尝试提取它以使问题可见...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-04
    • 1970-01-01
    • 2019-01-28
    • 2017-09-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多