我正在使用一个有很多怪癖的旧数据库,这是我尝试构建可与Grails 2.5.6一起使用的数据访问库时遇到的最新问题。
我有一个User
域类,而不是在数据库中拥有自己的表,而是通过UserHistory
类将新记录保存到历史表中。在历史表中,最新记录由changeType
属性指示:每个用户只有一个记录,而changeType
为空,这是最新记录。因此,为了保存User
对象,必须使用非空的changeType
更新当前的最新记录,并且必须以changeType
为空插入新记录。
我遇到的问题是,当我保存User
对象时,显然在执行为刷新注册的操作时,它抛出了ConcurrentModificationException
。下面有更多详细信息。
这是所讨论的两个类的概述:
// User.groovy
class User {
static mapping = {
id name: 'pidm', generator: 'assigned'
}
Long pidm
String firstName
@Lazy
Set<UserHistory> histories = { UserHistory.findAllByPidm(pidm) }()
def beforeUpdate()
{
// Mark the current UserHistory object with the appropriate change type
UserHistory currentHistory = histories.find({ it.changeType == null })
currentHistory.changeType = 'N'
// Create a new UserHistory object with the changes applied
UserHistory newHistory = new UserHistory(pidm: pidm, firstName: firstName)
// Save the two UserHistory objects.
currentHistory.save()
newHistory.save(insert: true)
// Return false so we don't try to save the User
return false
}
}
// UserHistory.groovy
class UserHistory {
Long pidm
String firstName
}
我的集成测试如下:
// UserIntegrationSpec.groovy
void "test saving a name change"()
{
when:
def user = User.get(pidm)
then:
user.firstName == oldName
when:
def oldHistoryCount = user.histories.size()
user.firstName = newName
user.save() // throws exception
def user2 = User.read(pidm)
then:
user2.firstName == newName
user2.histories.size() == oldHistoryCount + 1
where:
pidm | oldName | newName
123 | "David" | "Barry"
}
在
beforeUpdate()
执行之后但在user.save()
完成之前引发异常:Failure: |
test saving a name change(UserIntegrationSpec)
|
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at java.util.Collections$UnmodifiableCollection$1.next(Collections.java:1042)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:463)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:351)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:350)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:56)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1258)
at org.codehaus.groovy.grails.orm.hibernate.metaclass.SavePersistentMethod.flushSession(SavePersistentMethod.java:87)
at org.codehaus.groovy.grails.orm.hibernate.metaclass.SavePersistentMethod$1.doInHibernate(SavePersistentMethod.java:60)
at org.codehaus.groovy.grails.orm.hibernate.GrailsHibernateTemplate.doExecute(GrailsHibernateTemplate.java:188)
at org.codehaus.groovy.grails.orm.hibernate.GrailsHibernateTemplate.execute(GrailsHibernateTemplate.java:132)
at org.codehaus.groovy.grails.orm.hibernate.metaclass.SavePersistentMethod.performSave(SavePersistentMethod.java:56)
at org.codehaus.groovy.grails.orm.hibernate.metaclass.AbstractSavePersistentMethod.doInvokeInternal(AbstractSavePersistentMethod.java:215)
at org.codehaus.groovy.grails.orm.hibernate.metaclass.AbstractDynamicPersistentMethod.invoke(AbstractDynamicPersistentMethod.java:69)
at org.codehaus.groovy.grails.orm.hibernate.HibernateGormInstanceApi.save(HibernateGormInstanceApi.groovy:196)
at UserIntegrationSpec.test saving a name change(UserIntegrationSpec.groovy:43)
我已经尝试了对
flush
的三个调用中的save()
参数的每个排列。这要么没有影响,要么推迟了持久性,以至于检查历史长度的assert语句失败。我也尝试过使用User.withSession { Session session -> session.flush() }
手动刷新 session ;这将引发相同的ConcurrentModificationException
。有什么我想念的吗?如何实现我要执行的操作?还是有人可以建议另一种方法?
最佳答案
通过执行以下操作,我设法解决了这一问题:
newHistory
添加到User
的历史记录中flush: false
保存两个历史记录flush: true
保存用户因此,似乎不刷新嵌套存储和刷新外部存储的组合是正确的选择,但是我的测试用例仍然失败,因为它正在查看缓存的集合,而不是从数据库中重新获取它。
这是更新的
beforeUpdate
方法:def beforeUpdate()
{
// Mark the current UserHistory object with the appropriate change type
UserHistory currentHistory = histories.find({ it.changeType == null })
currentHistory.changeType = 'N'
// Create a new UserHistory object with the changes applied
UserHistory newHistory = new UserHistory(pidm: pidm, firstName: firstName)
histories.add(newHistory) // this line is the key to the solution
// Save the two UserHistory objects.
currentHistory.save(flush: false)
newHistory.save(flush: false, insert: true)
// Return false so we don't try to save the User
return false
}
现在我的测试和代码都可以正常工作。 (至少在这个区域)
关于hibernate - Grails/GORM:避免在嵌套保存调用上发生ConcurrentModificationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48081696/