域对象实例上的第一个save()是否持久?
环境:我相信Grails 3.3.5,GORM 6.1.9,PostgreSQL 9.5,JDK 1.8.0_171,Ubuntu 16.04。
我的应用程序基于来自多个文本文件的输入来创建Recital(域对象)实例。这是数据库负载,因此所有事情都在一个单一的控制器调用(调用服务方法)中发生。一笔交易。
服务方法解析输入流以查找演奏会。当找到一个时,它尝试一种 findBy 方法(引用编号被编号)。如果有一个具有给定编号的演奏会,它将从输入中对其进行更新并执行save()。否则,它将根据输入创建一个新实例并执行save()。 failOnError 随处可见。演奏会不属于任何东西,没有级联问题。
程序逻辑无法判断修改是否是实例上的最后更新。因此,每次更改后都有一个save()。但这是行不通的。看来,您只能保存一次()。
在数据库中找到的最终结果是只有第一个save()得以保留。在调试正在运行的程序时,我可以验证实例在内存中是否已完全更新。
如果这是正确的,则文档应显示: save方法通知持久化上下文应以当前状态保存或更新实例。此后(在同一事务中)的所有修改都将被忽略。 除非使用flush参数,否则不会立即保留该对象 ...
P.S。 save()文档在刷新持久性上下文时解释了“ flush:true”。这对新来者几乎没有解释价值。一些“冲洗”同义词是:清洗,擦除,清除,清除,清除,擦除。数据库人员可能将其作为ROLLBACK而不是COMMIT来获取。当然,解释是Hibernate术语。如果Grails doco主要是独立的,那就太好了。 (同一文档中的附带问题。)
答案 0 :(得分:1)
在有太多人为此浪费时间之前,这里有一些新见解。
碰巧的是,演奏会域具有嵌入式组件。我的应用程序中的更新仅影响嵌入式组件的成员。
我已验证对于同一实例的三个特定更新,成员在内存中的更新正确。但是,在三个更新中的每一个更新之后,recital.isDirty()返回false,同样是recital.isDirty('body'),其中 body 是嵌入式组件。
我的结论是,更新嵌入式组件不一定会在实例上设置脏标志。因此它不会保存。忘记最初问题中提出的理论。似乎有一个Grails / GORM错误。
我目前的解决方法是用 executeUpdate 替换更新。犹豫不决,但我花了太多时间在此上,必须继续前进。
编辑:不是错误。 GORM现在要求使用@DirtyCheck
注释嵌入式类,以进行脏检查。结案了。
答案 1 :(得分:0)
仅当我不参与交易时,我才能看到所描述的行为,无论如何这都是一种不好的做法。因此,我怀疑您不在事务中或不是一直在事务中。 您确定所有涉及的方法都用@Transactional注释吗?
flush: true
的含义不是放置提交,而是执行对数据库的插入/更新,因此Tuomas的建议应该可行,但是如果我没错,那么您仍然不在事务范围之内如果发生异常,则说明您已部分保存了数据。
仅当您保留所有带有@Transactional注释的方法时,才提交提交,但是如果存在未捕获的RunTimeException,则将得到回滚。
在同一笔交易中考虑这一点:
Recital.save(); Racital.count()
-> 0
Recital.save(flush: true); Racital.count()
-> 1
如果查看SQL日志,则可以看到仅保存了新对象的第一个.save(),所有更新都将保留,直到您离开事务为止,这可以节省开销。我想,由于您不在事务中,因此上一次更新将永远不会执行。
顺便说一下,IMO grails文档是我所知道的最好的OSS项目之一。如果稍后再查看,可能会看到有关陷阱的提示,但是并不会到处重复。