Java 获得;org.hibernate.LazyInitializationException“;从我的二级ehcache检索项目后出现异常
我将Hibernate5.1.0.Final与ehcache和Spring3.2.11.RELEASE一起使用。我在我的一个Java 获得;org.hibernate.LazyInitializationException“;从我的二级ehcache检索项目后出现异常,java,spring,hibernate,ehcache,lazy-initialization,Java,Spring,Hibernate,Ehcache,Lazy Initialization,我将Hibernate5.1.0.Final与ehcache和Spring3.2.11.RELEASE一起使用。我在我的一个DAOs中设置了以下@Cacheable注释: @Override @Cacheable(value = "main") public Item findItemById(String id) { return entityManager.find(Item.class, id); } 正在返回的项有许多关联,其中一些是惰性的。例如,它(最终)引用字段: @Man
DAO
s中设置了以下@Cacheable
注释:
@Override
@Cacheable(value = "main")
public Item findItemById(String id)
{
return entityManager.find(Item.class, id);
}
正在返回的项有许多关联,其中一些是惰性的。例如,它(最终)引用字段:
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "product_category", joinColumns = { @JoinColumn(name = "PRODUCT_ID") }, inverseJoinColumns = { @JoinColumn(name = "CATEGORY_ID") })
private List<Category> categories;
堆栈跟踪是:
16:29:42,557 INFO [org.directwebremoting.log.accessLog] (ajp-/127.0.0.1:8009-18) Method execution failed: : org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: org.mainco.subco.ecom.domain.Product.standardCategories, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:579) [hibernate-myproject-5.1.0.Final.jar:5.1.0.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:203) [hibernate-myproject-5.1.0.Final.jar:5.1.0.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:558) [hibernate-myproject-5.1.0.Final.jar:5.1.0.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:131) [hibernate-myproject-5.1.0.Final.jar:5.1.0.Final]
at org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:277) [hibernate-myproject-5.1.0.Final.jar:5.1.0.Final]
at org.mainco.subco.ebook.service.ContentServiceImpl.getCorrelationsByItem(ContentServiceImpl.java:957) [myproject-90.0.0-SNAPSHOT.jar:]
at org.mainco.subco.ebook.service.ContentServiceImpl.getContent(ContentServiceImpl.java:501) [myproject-90.0.0-SNAPSHOT.jar:]
at sun.reflect.GeneratedMethodAccessor819.invoke(Unknown Source) [:1.6.0_65]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) [rt.jar:1.6.0_65]
at java.lang.reflect.Method.invoke(Method.java:597) [rt.jar:1.6.0_65]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317) [spring-aop-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183) [spring-aop-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) [spring-aop-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96) [spring-tx-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260) [spring-tx-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94) [spring-tx-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) [spring-aop-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91) [spring-aop-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) [spring-aop-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204) [spring-aop-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at com.sun.proxy.$Proxy126.getContent(Unknown Source)
我理解Hibernate会话关闭了什么-我不关心为什么会发生这种情况。此外,这不是使上述关联变得急切(而不是懒惰)的选项。既然如此,我该如何解决这个问题
编辑:下面是如何配置我的ehccahe.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd" updateCheck="false">
<!-- This is a default configuration for 256Mb of cached data using the JVM's heap, but it must be adjusted
according to specific requirement and heap sizes -->
<defaultCache maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="86400"
timeToLiveSeconds="86400"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
<cache name="main" maxElementsInMemory="10000" />
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=automatic, multicastGroupAddress=230.0.0.1,
multicastGroupPort=4446, timeToLive=32"/>
<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
properties="hostName=localhost, port=40001,
socketTimeoutMillis=2000"/>
</ehcache>
下面是我如何将它插入到我的Spring上下文中
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="packagesToScan" value="org.mainco.subco" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
</property>
<property name="dataSource" ref="dataSource"/>
<property name="jpaPropertyMap" ref="jpaPropertyMap" />
</bean>
<cache:annotation-driven key-generator="cacheKeyGenerator" />
<bean id="cacheKeyGenerator" class="org.mainco.subco.myproject.util.CacheKeyGenerator" />
<bean id="cacheManager"
class="org.springframework.cache.ehcache.EhCacheCacheManager"
p:cacheManager-ref="ehcache"/>
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
p:configLocation="classpath:ehcache.xml"
p:shared="true" />
<util:map id="jpaPropertyMap">
<entry key="hibernate.show_sql" value="false" />
<entry key="hibernate.hbm2ddl.auto" value="validate"/>
<entry key="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
<entry key="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup" />
<entry key="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>
<entry key="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider"/>
<entry key="hibernate.cache.use_second_level_cache" value="true" />
<entry key="hibernate.cache.use_query_cache" value="false" />
<entry key="hibernate.generate_statistics" value="false" />
</util:map>
<bean id="entityManager" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
看一看。基本上,您的缓存不是Hibernate二级缓存。您正在访问分离实体实例上的惰性未初始化关联,因此预计会引发LazyInitializationException
您可以尝试使用,但建议的方法是进行配置,以便:
- 缓存的实体将自动附加到加载它们的后续会话
- 缓存的数据在更改时会在缓存中自动刷新/失效
- 在考虑事务语义的情况下,对缓存实例的更改是同步的。更改对具有所需缓存/db级别的其他会话/事务可见
- 当从与缓存实例关联的其他实体导航到缓存实例时,缓存实例将自动从缓存中获取
@Override
@Cacheable(value = "main")
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Item findItemById(String id) {
Item result = entityManager.find(Item.class, id);
Hibernate.initialize(result.getAssociation1());
Hibernate.initialize(result.getAssociation2());
return result;
}
由于Spring事务代理(拦截器)可能在缓存代理之前执行(两者都具有相同的默认顺序
值:;),因此您总是会启动嵌套事务,无论是真正获取实体,还是仅返回缓存实例
虽然我们可以得出结论,启动不需要的嵌套事务的性能损失很小,但这里的问题是,当缓存中存在托管实例时,您会留下一个很小的时间窗口
为了避免这种情况,您可以更改默认订单值:
<tx:annotation-driven order="200"/>
<cache:annotation-driven order="100"/>
所以缓存拦截器总是放在事务拦截器之前
或者,为了避免排序配置更改,您可以简单地将调用从@Cacheable
方法委托给另一个bean上的@Transactional(propagation=propagation.REQUIRES_NEW)
方法问题是您正在缓存对延迟加载的对象的引用。在对象全部加载后缓存该对象,或者根本不使用缓存 以下是如何在缓存类别之前手动加载类别:
Item item = entityManager.find(Item.class, id);
item.getParent().getProduct().getCategories();
return item;
还有一个更好的缓存策略是让缓存位于应用程序的服务级别,而不是DAO级别或根本不缓存
您的问题是由以下事件引起的:
正在检索没有其类别的项,然后将其放入事务1中的缓存中。在事务2中,调用相同的方法并检索项目并尝试读取其类别。此时,hibernate尝试从事务1中读取与Item对象关联的类别,但事务1已完成,因此失败。您在代码片段中实现的是基于spring缓存的自定义缓存。在您的实现中,您需要处理缓存逐出,确保在对象图被缓存时正确加载它们,等等。一旦它们被缓存并且加载它们的原始hibernate会话关闭,它们将被分离,您将无法再导航未缓存的惰性关联。此外,当前状态下的自定义缓存解决方案将缓存实体图,这可能不是您想要的,因为该图的任何部分都可能在给定的时间发生更改,您的缓存解决方案将需要监视该图所有部分的更改,以正确处理逐出 您在问题中发布的配置不是休眠二级缓存 管理缓存是一项复杂的工作,我不建议您自己去做,除非您完全确定自己在做什么(但这样您就不会在Stackoverflow上问这个问题) 让我解释一下当您得到
LazyInitializationException
时发生了什么:您将一个dao方法标记为@org.springframework.cache.annotation.Cacheable
。在这种情况下会发生以下情况:
Item item = entityManager.find(Item.class, id);
item.getParent().getProduct().getCategories();
return item;
<entry key="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>
<entry key="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider"/>
<entry key="hibernate.cache.use_second_level_cache" value="true" />
<entry key="javax.persistence.sharedCache.mode" value="ENABLE_SELECTIVE" />
<persistence-unit name="default">
<!-- other configuration lines stripped -->
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
<!-- other configuration lines stripped -->
</persistence-unit>
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "product_category", joinColumns = { @JoinColumn(name = "PRODUCT_ID")
}, inverseJoinColumns = { @JoinColumn(name = "CATEGORY_ID") })
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private List<Category> categories;
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
spring.jpa.open-in-view=true
spring.cache.type=simple