With a JEE 7 setup using JSF, CDI, EJB + JPA I ran into the issue "failed to lazily initialize a collection". I found a solution but would like to confirm that it is a proper solution and that I correctly understood the problem.
The setup basically is:
JSF -> CDI Bean -> EJB3 -> JPA
In the JSF page I basically do:
<ui:repeat value="#{dishService.dishes}" var="dish">
<ui:repeat value="#{dish.ingredients}" var="ingredient">
#{ingredient.name}
</ui:repeat>
</ui:repeat>
The CDI Bean:
@Dependent
@Named
public class DishService {
@Inject
private DatabaseServiceLocal databaseService;
public List<Dish> getDishes() {
return databaseService.getDishes();
}
}
The 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();
}
}
The JPA entity Dish:
@OneToMany(mappedBy = "dish",
cascade = CascadeType.PERSIST,
orphanRemoval = true)
private List<Ingredient> ingredients = Collections.emptyList();
and Ingredient:
@ManyToOne
private Dish dish;
Running this produces the error:
failed to lazily initialize a collection of role: entity.Dish.ingredients, could not initialize proxy - no Session
To my understanding, this happens because the JTA container managed transaction ends when the public method getDishes()
of the EJB completes. The JPA entities get detached and the session closed, and it is not possible any more to lazily load the ingredients in the JSF page.
After some research, it seemed that the most commonly accepted solution for this is JOIN FETCH
. So I changed my JPA query to
entityManager.createQuery("SELECT DISTINCT d from Dish d JOIN FETCH d.ingredients");
And the problem is solved. I had to add the DISTINCT
since without it, the query produces the cartesian product of all Dishes and Ingredients, which is expected I suppose.
So finally the question: Did I understand the problem correctly and is JOIN FETCH
the proper solution?
private List<Ingredient> ingredients ;
. – Omar