2014-04-21 67 views
1

使用JSF,CDI,EJB + JPA进行JEE 7设置时遇到了“未能懒惰地初始化集合”的问题。我找到了一个解决方案,但想确认这是一个合适的解决方案,并且我正确地理解了这个问题。JSF,CDI,EJB + JPA和事务处理

的设置主要是:

JSF - > CDI豆 - > EJB3 - > JPA

在JSF页面我基本上做到:

<ui:repeat value="#{dishService.dishes}" var="dish"> 
    <ui:repeat value="#{dish.ingredients}" var="ingredient"> 
     #{ingredient.name} 
    </ui:repeat> 
</ui:repeat> 

的CDI豆:

@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 .reredients,无法初始化代理 - 无会话

据我了解,这是因为JTA容器管理事务在EJB的公共方法getDishes()完成时结束。 JPA实体被分离并关闭会话,并且不可能再延迟地加载JSF页面中的成分。

经过一番研究,似乎最普遍接受的解决方案是JOIN FETCH。所以我将我的JPA查询更改为

entityManager.createQuery("SELECT DISTINCT d from Dish d JOIN FETCH d.ingredients"); 

并且问题解决了。我不得不添加DISTINCT,因为如果没有它,查询会生成所有菜肴和配料的笛卡尔积,这是我想的。

因此最后的问题是:我是否正确理解问题并且是JOIN FETCH正确的解决方案?

+0

我认为它可以通过在持久层中删除初始化来实现:'private List ingredients;'。 – Omar

+0

我尝试了没有初始化,即使它不需要,删除它并不能解决问题。 –

回答

3

是的,在查询上设置联合提取是最合适的方式。

您还可以使用实体图形,这是JPA 2.1中的一项新功能来执行抓取。

发生这种情况的原因是事务在您的CDI托管bean中启动。 JSF将调用托管bean,但是它有几个包装器。它试图获取事务外的值。

1

对于@Many类型的关系,默认FetchTypeLAZY,这意味着List<Ingredient>将不会被加载,直到它的访问的第一时间。

FetchType fetch (Optional) Whether the association should be lazily loaded or must be eagerly fetched. The EAGER strategy is a requirement on the persistence provider runtime that the associated entities must be eagerly fetched. The LAZY strategy is a hint to the persistence provider runtime.

Default value: javax.persistence.FetchType.LAZY

Since: JPA 1.0

是的,对于您的情况,JOIN FETCH解决方案看起来不错。