我正在使用一个有许多怪癖的遗留数据库,这是我尝试构建一个与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()
参数的每个排列。这要么没有区别,要么推迟持久性,以便检查历史长度的断言语句失败。我还尝试用User.withSession { Session session -> session.flush() }
手动刷新会话;这会抛出相同的ConcurrentModificationException
。
有什么我想念的吗?我怎样才能实现我想要做的事情?或者有人可以提出另一种方法吗?
答案 0 :(得分:0)
我设法通过执行以下操作来解决此问题:
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
}
现在我的测试和我的代码都正常工作。 (好吧,至少在这方面)