[英]Hibernate Caching and lazy loaded associations
我得到了一个典型的订单和项目实体关联。 为了使只读订单成为可能,项目集默认为 FetchType.LAZY。 启用第二级和查询缓存。 为了读取带有关联商品的订单,我使用了 JPQL 查询。 查询和实体由 EHCache 缓存。 但是在访问项目时的第二次调用时,抛出了 LazyInitializationException,因为项目未初始化(未从缓存中恢复)。 为什么? 实现此要求的最佳方法是什么?
命令:
@Entity
@Cacheable
@NamedQueries({
@NamedQuery(name = Order.NQ_FIND_BY_ID_FETCH_ITEMS, query = "SELECT DISTINCT o FROM Order o JOIN FETCH o.items WHERE o.id = :id")
})
@Table(...)
public class Order extends ... {
...
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
// @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private Set<Item> items = new HashSet<Item>();
...
}
物品:
@Entity
@Cacheable
@Table(...)
public class Item extends ... {
@ManyToOne
@JoinColumn(name = "order_id", nullable = false)
private Order order;
...
}
道:
public class OrderDaoJpaImpl extends ... {
@Override
public Catalog findByIdFetchItems(Long id) {
TypedQuery<Order> query = entityManager.createNamedQuery(Order.NQ_FIND_BY_ID_FETCH_ITEMS, Order.class);
query.setParameter("id", id);
// query.setHint(QueryHints.HINT_CACHEABLE, Boolean.TRUE);
Order order = JPAUtil.singleResultOrNull(query);
return order;
}
服务:
@Service("orderService")
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class OrderServiceImpl implements OrderService {
@Override
public Order getOrderWithItems(Long orderId) {
return orderDao.findByIdFetchItems(orderId);
}
}
持久性.xml:
<persistence ...>
<persistence-unit name="shop-persistence" transaction-type="RESOURCE_LOCAL">
<jar-file>shop-persistence.jar</jar-file>
<!-- Enable JPA 2 second level cache -->
<shared-cache-mode>ALL</shared-cache-mode>
<properties>
...
<property name="hibernate.cache.use_second_level_cache" value="true" />
<property name="hibernate.cache.use_query_cache" value="true" />
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"/>
</properties>
</persistence-unit>
</persistence>
Spring 框架 4.3.7.RELEASE 和 Hibernate 5.2.9.Final。
如您所见,我尝试使用 Hibernate 实体注释和缓存提示而不是 JPA 缓存。 我也尝试过 JPA 实体图而不是 JOIN FETCH。 始终相同:在第二次调用订单查询时不会初始化/恢复项目。
由于HHH-12430问题,抛出LazyInitializationException
。
但是,您的代码也存在一些问题,在修复 Hibernate HHH-12430
问题之前,您可以使用一种解决方法。
当您使用 Hibernate 时,仅使用@Cacheable
注释您的实体是@Cacheable
。
您还需要提供CacheConcurrencyStrategy
:
@Entity
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
Query Cache 只存储实体标识符,并且由于您只选择Order
, items
关联也不会被缓存。
您可以做的是将您的查询更改为:
@NamedQuery(name = Order.NQ_FIND_BY_ID_FETCH_ITEMS, query = "SELECT DISTINCT o FROM Order o WHERE o.id = :id")
然后,激活缓存以进行收集:
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private Set<Item> items = new HashSet<Item>();
Order
和Item
都应该使用这个:
@Entity
@org.hibernate.annotations.Cache(usage =
CacheConcurrencyStrategy.READ_WRITE)
因为它们需要可缓存。
并确保在返回结果集之前初始化items
集合:
Order order = JPAUtil.singleResultOrNull(query);
if(order != null) {
order.getItems().size();
}
return order;
这样, items
将始终被初始化,并且集合将从缓存中获取,而不是从数据库中获取。
我认为 @Cacheable 不是 Hibernate 缓存,而是 Spring 缓存。 @Cache 注释用于 Hibernate 缓存。 除此之外,当我遇到这些问题时,为了获得也可用于缓存的 JOIN FETCH 结果,我不得不将 Hibernate.initialize(...) 添加到 Dao 方法以避免 LazyInitializationException。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.