我正在努力解决以下问题:
我有一个无状态bean作为实体的存储库,这个bean有一个实体管理器声明。
当我从另一个无状态bean调用这个bean时,返回一个实体,然后如果在这个新返回的实体中调用一个关系,就会抛出异常" org.hibernate.LazyInitializationException:无法懒惰地初始化一个集合角色"。据我所知,持久化上下文附加到事务,或者如果事务不存在则创建一个新事务,但在这种情况下,事务存在并且它在调用存储库bean的客户端无状态bean中启动。
这是一个简单的例子:
@Entity
public class Config{
Long id;
String description;
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="equipment")
private Equipment equipment;
}
@Entity
public class Equipment{
Long id;
String name;
@OneToMany(mappedBy = "equipment")
Config config;
}
@Stateless
public class EquipmentRepo{
@PersistenceContext(type=PersistenceContextType.TRANSACTION)
EntityManager em;
public Equipment find(Long id) {
return em.find(Equipment.class, id);
}
}
@Stateless
public class ServiceFacade {
@Inject
EquipmentRepo repo;
public List<Config> findEquipmentConfig(Long id) {
Equipment element = repo.find(id);
List<Config> configurations = element.getConfig();
return configurations;
}
}`
答案 0 :(得分:0)
延迟初始化还需要活动事务。如果您仔细检查,异常的原因将与缺少事务有关。
当无状态方法返回时,活动事务结束,并且由于事务是容器管理的,因此当您调用实体方法加载引用的延迟加载属性时,hibernate需要活动事务。
通常通过确保您的实体域未在事务/服务级别之外使用来解决此问题。当服务返回时,它应该返回映射实体的dto,带有必填字段。因此,在这种情况下,如果您需要延迟加载的属性,它仍将在同一事务中执行并映射到您自己的dto。
答案 1 :(得分:0)
首先,我假设你的Equipment
课程应该是这样的:
@Entity
public class Equipment{
Long id;
String name;
@OneToMany(mappedBy = "equipment")
List<Config> config;
}
即config
应该是一个集合。
@OneToMany
关系默认是懒惰地初始化。
执行时:
Equipment element = repo.find(id);
它返回一个Equipment
个对象,该对象具有Config
代理集合,而不是实体本身。
如果您在element.config
方法中迭代findEquipmentConfig
,那么JPA实现将在引用时加载每个Config实体。
这是有效的,因为实体管理器和事务仍处于活动状态。
但是你从调用findEquipmentConfig
的方法开始迭代,我想这是一个servlet或其他一种没有事务上下文活动的web控制器。
因此,您获得了org.hibernate.LazyInitializationException
。
此时您可能正在考虑添加一个虚拟加载循环来预加载所有配置。
但这是一个坏主意,因为它会导致臭名昭着的N + 1 SELECT问题和一个难以扩展的应用程序。如果设备实体有1000个与之关联的配置项,那么您的应用程序将执行1001个SELECT语句来加载该对象(额外的一个是加载初始的Equipment实体)。
这实际上是JPA QL中提供的join fetch
查询语法的动机之一。
您可以更改find
方法,使其看起来像:
public Equipment find(Long id) {
TypedQuery<Equipment> query = em.createQuery(
"SELECT e FROM Equipment e LEFT JOIN FETCH e.config WHERE e.id = :id", Equipment.class);
return query.setParameter("id", id).getSingleResult();
}
这将确保您的所有设备配置立即加载。