【问题标题】:Why do Grails command objects commit changes to domain objects by default?为什么 Grails 命令对象默认提交对域对象的更改?
【发布时间】:2016-05-16 23:22:11
【问题描述】:

如果我在我的 Grails 命令对象中使用 GORM 域对象,命令对象会自动提交对域对象的更改,即使我没有调用 save() 方法。

我想绑定到命令对象中的 GORM 对象,但不保存或提交对数据库的更改。如果我的控制器或我的服务抛出异常,我想要事务回滚。

我可以通过以下注释强制执行我想要的行为,但这感觉就像我正在努力做到这一点。

Controller Class = @Transactional(readOnly = true)
Controller action method = @Transactional
Command Object Class = @Transactional(readOnly = true)
Service Class = @Transactional

我做错了什么吗,Grails 域对象是否应该由命令对象自动提交,除非我添加所有这些注释?

【问题讨论】:

    标签: grails grails-orm


    【解决方案1】:

    这并不特定于命令对象,它是控制器动作的一般特征。默认情况下,open-session-in-view 模式处于活动状态,其中在操作运行之前创建 Hibernate Session 并绑定到线程本地,并在操作完成后刷新并关闭它。从数据库中检索到的任何持久实例(无论是由于查询而显式地检索,还是在数据绑定期间隐式地检索)都将保持连接到打开的会话,并在会话刷新时进行脏检查。任何已修改的实例都将与其他排队操作一起刷新其更改,无论是否调用save()。

    使整个方法(或类)具有事务性和只读性可能是矫枉过正。更直接的方法是将实例检索为只读,例如使用read() 而不是get(),在执行条件查询等时调用readOnly 方法,或者通过在每个实例上调用discard() 方法来“分离”修改后的实例。另一种选择是在操作结束时清除会话,这样就不会自动刷新,例如

    AnyDomainClass.withSession { it.clear() }
    

    请注意,在“只读”模式下检索的实例可以保留其更改,但 Hibernate 不会自动对这些实例执行任何操作,只有当您显式调用 save() 时才会发生这种情况。

    【讨论】:

    • 但我看到的是,绑定在命令对象中的对象隐式地在命令对象之后和控制器操作的第一行之前提交,而不是在操作结束时提交。除非我像上面那样注释,否则我似乎在命令对象中得到一个事务,而在动作中得到另一个事务。
    • 你能创建一个小的测试应用来演示这个吗?我想看看,所以如果你可以在 Github 或其他在线 repo 上运行一些东西,或者甚至压缩代码并通过电子邮件发送给我,我希望能找到它。如果这是一个真正的错误,最好报告它。
    • 好的,找到了!演示应用程序没有相同的行为。事实证明,我们是通过将 UtilService 注入命令对象以帮助验证而导致问题的。该服务没有注释@Transactional(readOnly = true),因此使用它开始一个事务并返回到控制器操作提交该事务。注释 UtilService @Transactional(readOnly = true) 解决了这个问题。
    猜你喜欢
    • 1970-01-01
    • 2014-06-07
    • 2013-10-24
    • 1970-01-01
    • 2017-08-24
    • 2012-12-09
    • 1970-01-01
    • 2010-12-24
    • 1970-01-01
    相关资源
    最近更新 更多