对于我的grails项目,我的大多数类都有一个内存中的h2数据库,但只为所有与用户相关的类添加了一个持久数据库。在启动时,应用程序执行多个线程。添加第二个数据源后,我得到了HibernateExceptions。具体来说,
Illegal attempt to associate a collection with two open sessions; nested exception is org.hibernate.HibernateException
它们只发生在前一个或两个线程中,然后其余的执行没有问题。这让我相信这是数据库首次启动时的某种并发问题。发生错误的代码是:
instance.customer = customerService.retrieveCustomer instance
instance.name = instance.customer?.name
Instance.withTransaction{
instance.customer.save() // <-- THIS LINE IS THE PROBLEM
instance.save()
}
我不知道这是数据库问题,hibernate问题还是其他问题。 运行Grails 2.0.4
完整Stacktrace:
| Error 2013-03-20 16:25:30,995 [pool-16-thread-1] ERROR kindlingcustomers.InstanceList - k_54 Illegal attempt to associate a collection with two open sessions; nested exception is org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions org.springframework.orm.hibernate3.HibernateSystemException: Illegal attempt to associate a collection with two open sessions; nested exception is org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions
at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:683)
at org.springframework.orm.hibernate3.HibernateAccessor.convertHibernateAccessException(HibernateAccessor.java:412)
at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:411)
at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:339)
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:212)
at org.codehaus.groovy.grails.orm.hibernate.metaclass.AbstractDynamicPersistentMethod.invoke(AbstractDynamicPersistentMethod.java:63)
at org.codehaus.groovy.grails.commons.metaclass.DynamicMethodInvocation$invoke.call(Unknown Source)
at org.codehaus.groovy.grails.orm.hibernate.HibernateGormInstanceApi.save(HibernateGormEnhancer.groovy:847)
at kindlingcustomers.Customer.save(Customer.groovy)
at kindlingcustomers.Customer$save$0.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:112)
at kindlingcustomers.InstanceList$__clinit__closure5_closure8.doCall(InstanceList.groovy:67)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.springsource.loaded.ri.ReflectiveInterceptor.jlrMethodInvoke(ReflectiveInterceptor.java:1231)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1047)
at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:1110)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:877)
at groovy.lang.Closure.call(Closure.java:412)
at kindlingcustomers.InstanceList$__clinit__closure5_closure8.call(InstanceList.groovy)
at org.codehaus.groovy.runtime.ConvertedClosure.invokeCustom(ConvertedClosure.java:51)
at org.codehaus.groovy.runtime.ConversionHandler.invoke(ConversionHandler.java:82)
at com.sun.proxy.$Proxy30.doInTransaction(Unknown Source)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:130)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.springsource.loaded.ri.ReflectiveInterceptor.jlrMethodInvoke(ReflectiveInterceptor.java:1231)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoCachedMethodSite.invoke(PojoMetaMethodSite.java:189)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:53)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
at org.grails.datastore.gorm.GormStaticApi.withTransaction(GormStaticApi.groovy:573)
at kindlingcustomers.Instance.withTransaction(Instance.groovy)
at kindlingcustomers.Instance$withTransaction.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
at kindlingcustomers.InstanceList$__clinit__closure5.doCall(InstanceList.groovy:66)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.springsource.loaded.ri.ReflectiveInterceptor.jlrMethodInvoke(ReflectiveInterceptor.java:1231)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1047)
at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:1110)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:877)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:921)
at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:1110)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:877)
at groovy.lang.Closure.call(Closure.java:412)
at groovy.lang.Closure.call(Closure.java:406)
at groovy.lang.Closure.run(Closure.java:490)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
at java.lang.Thread.run(Thread.java:680)
我的数据源:
environments {
development {
dataSource {
dbCreate = "create-drop" // one of 'create', 'create-drop', 'update', 'validate', ''
url = "jdbc:h2:mem:devDb;MVCC=TRUE"
}
dataSource_users {
dbCreate = "update"
url = "jdbc:h2:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000"
}
}
test {
dataSource {
dbCreate = "update"
url = "jdbc:h2:mem:testDb;MVCC=TRUE"
}
dataSource_users {
dbCreate = "update"
url = "jdbc:h2:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000"
}
}
production {
dataSource {
dbCreate = "create-drop"
url = "jdbc:h2:mem;MVCC=TRUE"
pooled = true
properties {
maxActive = -1
minEvictableIdleTimeMillis=1800000
timeBetweenEvictionRunsMillis=1800000
numTestsPerEvictionRun=3
testOnBorrow=true
testWhileIdle=true
testOnReturn=true
validationQuery="SELECT 1"
}
}
dataSource_users {
dbCreate = "update"
url = "jdbc:h2:prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000"
}
}
}
答案 0 :(得分:1)
我设法通过一个非常草率的解决方法来抑制异常。希望它会帮助其他人出现,但如果有人有其他解决方案或任何洞察问题,我很乐意听到。
无论如何,你想在适当的时候调用merge()而不是save()。我不知道如何确定是否跨多个会话引用了一个实例,所以我只是将每个保存包装在try-catch块中,然后如果有异常则调用merge()。这不是最漂亮的解决方案,但它有效。
def customerService = AH.application.mainContext.customerService
instance.customer = customerService.retrieveCustomer instance
instance.name = instance.customer?.name ?: instance.domain?.tokenize('.')[0].capitalize() ?: ''
Instance.withTransaction{
try{ instance.customer.save() }
catch (Exception e) { instance.customer.merge() }
try { instance.save() }
catch (Exception e) { instance.merge() }
}
答案 1 :(得分:1)
在Hibernate中,每个PersistedCollection
(Hibernate为任何集合关系设置的包装类)都只与一个Session
实例相关联。这是Hibernate确保维护实例标识Session
级别保证的工作的一部分。
您收到的错误表示您在一个Session
中加载了一个包含集合的实体,然后在另一个.save()
中尝试Session
它。这可能不起作用,除非您首先将该实体与其他Session
相关联,您可以通过调用otherSession.merge(entity)
来执行此操作。这就是你的解决方法“有效”的原因。
您应该查看一下Hibernate文档的this section,它处理从一个数据源到另一个数据源的复制。我不确定{/ 1}}是否可用于Ruby / Grails ......