2013-03-20 25 views
2

對於我的grails項目,我有一個用於大多數類的內存中h2數據庫,但是爲所有與用戶相關的類添加了一個持久數據庫。在啓動時,應用程序執行多個線程。在添加第二個數據源之後,我得到了HibernateExceptions。具體來說,導致HibernateException的多個數據源?

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

完整堆棧跟蹤:

| 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" 
     } 
    } 
} 

回答

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

在Hibernate中,每一個PersistedCollection(即休眠投入到位任何您的收藏關係的包裝類)恰好與一個Session實例相關聯。這是Hibernate確保Session實例身份的級別保證得以維持的工作的一部分。

你得到的錯誤指示您在一個Session加載一個實體的集合,然後試圖.save()它在另一個Session。這可能行不通,除非您先採取步驟將該實體與其他Session關聯起來,您可以通過調用otherSession.merge(entity)來完成。這就是爲什麼你的解決方法「有效」。

您應該看看Hibernate文檔this section,它處理從一個數據源到另一個數據源的複製。我不確定是否replicate在Ruby/Grails中可用...