在最近升级到Hibernate 5.1的现有Grails 3.1.15应用程序中,开始出现afterInsert(或其他)钩子的奇怪问题。经过几个小时的测试,我可以追溯到Hibernate 5.1 + PostgreSQL组合 - 问题与H2无法重现。要重现,请创建一个由2个域对象组成的简单应用程序 - 审计跟踪和用户,如下所示:
class Audit {
Date dateCreated
String auditData
}
class User {
String name
String email
def afterInsert() {
new Audit(auditData: "User created: $this").save()
}
}
上面的代码适用于Hibernate 4,但是,如果应用程序升级到Hibernate5插件+ Hibernate 5.1.x(使用5.1.0.Final和5.1.5.Final测试),上述场景将始终导致尝试保存新的User
实例时出现ConcurrentModificationException。您可以使用脚手架控制器来重现。请注意,这只发生在PostgreSQL作为数据源的情况下 - 使用H2它仍然可以正常工作。
现在,根据GORM Docs(参见第8.1.3章),当尝试在beforeUpdate或afterInsert挂钩中保存其他对象时,应该使用新会话:
def afterInsert() {
Audit.withNewSession() {
new Audit(auditData: "User created: $this").save()
/* no exception logged, but Audit instance not preserved */
}
}
但这并不能真正解决PSQL的问题。异常消失,User
实例保持不变,但Audit
实例未保存。同样,这段代码可以和H2一起使用。要真正避免PSQL的问题,您必须在afterInsert中手动刷新会话:
def afterInsert() {
Audit.withNewSession() { session ->
new Audit(auditData: "User created: $this").save()
session.flush()
/* finally no exceptions, both User and Audit saved */
}
}
问题:这是一个错误,还是这个预期?我觉得有点怀疑需要手动刷新才能保持Audit
实例 - 当我看到它没有与H2冲洗并且似乎只影响PostgreSQL时更是如此。但我无法找到任何报告 - 任何指针都会受到赞赏!
为了完整起见,我使用PostgreSQL的以下JDBC驱动程序版本进行了测试:
runtime 'org.postgresql:postgresql:9.3-1101-jdbc41'
runtime 'org.postgresql:postgresql:9.4.1208.jre7'
runtime 'org.postgresql:postgresql:42.0.0'
对于升级到Hibernate 5.1,使用了以下依赖项:
classpath "org.grails.plugins:hibernate5:5.0.13"
...
compile "org.grails.plugins:hibernate5:5.0.13"
compile "org.hibernate:hibernate-core:5.1.5.Final"
compile "org.hibernate:hibernate-ehcache:5.1.5.Final"