【发布时间】:2018-01-03 16:50:47
【问题描述】:
我正在使用一个有很多怪癖的遗留数据库,这是我尝试构建一个与 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)
在对save() 的三个调用中,我已经尝试了flush 参数的所有排列。这要么没有区别,要么推迟持久化,使得检查历史长度的断言语句失败。我还尝试使用User.withSession { Session session -> session.flush() } 手动刷新会话;这会抛出相同的ConcurrentModificationException。
我有什么遗漏吗?我怎样才能实现我正在尝试做的事情?或者还有其他人可以建议的方法吗?
【问题讨论】:
-
你试过 currentHistory.save(flush : true) 吗?
-
在
beforeUpdate中,不要从域对象的集合中获取currentHistory,而是尝试从数据库中获取它,例如UserHistory.findByPidmAndChangeType(...)然后更新检索到的记录 -
@NitinDhomse 我确实尝试过。我在对
save()的三个调用中尝试了flush的所有排列。 -
@MikeW 在实施我的修复之前我没有看到你的评论(见答案)。它现在正在运行,所以我不愿意再胡思乱想了,但你的想法可能会奏效。
标签: hibernate grails grails-orm