使用JSF,CDI,EJB + JPA进行JEE 7设置我遇到了问题"未能懒惰地初始化集合"。我找到了一个解决方案,但我想确认这是一个正确的解决方案,并且我正确地理解了这个问题。
设置基本上是:
JSF - > CDI Bean - > EJB3 - > JPA
在JSF页面中我基本上做了:
<ui:repeat value="#{dishService.dishes}" var="dish">
<ui:repeat value="#{dish.ingredients}" var="ingredient">
#{ingredient.name}
</ui:repeat>
</ui:repeat>
CDI Bean:
@Dependent
@Named
public class DishService {
@Inject
private DatabaseServiceLocal databaseService;
public List<Dish> getDishes() {
return databaseService.getDishes();
}
}
EJB:
@Stateless
public class DatabaseService implements DatabaseServiceLocal, DatabaseServiceRemote {
@PersistenceContext(unitName = "dishlist")
private EntityManager entityManager;
public List<Dish> getDishes() {
final Query query = entityManager.createQuery("SELECT d FROM Dish d");
return query.getResultList();
}
}
JPA实体菜:
@OneToMany(mappedBy = "dish",
cascade = CascadeType.PERSIST,
orphanRemoval = true)
private List<Ingredient> ingredients = Collections.emptyList();
和成分:
@ManyToOne
private Dish dish;
运行它会产生错误:
未能懒惰地初始化角色集合:entity.Dish.ingredients,无法初始化代理 - 无会话
据我了解,这是因为当EJB的公共方法getDishes()
完成时,JTA容器管理的事务结束。 JPA实体被分离并且会话关闭,并且不可能再懒得加载JSF页面中的成分。
经过一些研究,似乎最普遍接受的解决方案是JOIN FETCH
。所以我将JPA查询更改为
entityManager.createQuery("SELECT DISTINCT d from Dish d JOIN FETCH d.ingredients");
问题解决了。我不得不添加DISTINCT
,因为没有它,查询会产生所有菜肴和配料的笛卡尔积,我想这是预期的。
最后一个问题是:我是否正确理解了问题并且是JOIN FETCH
正确的解决方案?
答案 0 :(得分:3)
是的,在查询上设置连接提取是最合适的方式。
您还可以使用实体图表(JPA 2.1中的新功能)来进行提取。
发生这种情况的原因是事务在CDI托管bean中启动。 JSF将调用托管bean,但它周围有几个包装器。它试图获取交易之外的值。
答案 1 :(得分:1)
对于@Many
类型的关系,默认FetchType
为LAZY
,这意味着List<Ingredient>
在第一次访问之前不会加载。
FetchType fetch(可选)关联是否应该是懒惰的 装载或必须急切地取出。 EAGER战略是一项要求 在关联实体必须的持久性提供程序运行时上 急切地抓住。 LAZY策略暗示了持久性 提供者运行时
默认值:javax.persistence.FetchType.LAZY
自:JPA 1.0
是的,对于您的情况,JOIN FETCH
解决方案看起来不错。