2014-04-21 4 views
1

С настройкой JEE 7 с использованием JSF, CDI, EJB + JPA Я столкнулся с проблемой «не удалось лениво инициализировать коллекцию». Я нашел решение, но хотел бы подтвердить, что это правильное решение и что я правильно понял проблему.JSF, CDI, EJB + JPA и транзакция

Установка в основном это:

JSF -> КДИ Bean -> EJB3 -> JPA

На странице JSF я в принципе:

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

КДИ 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, не удалось инициализировать прокси - нет Сессия

Насколько я понимаю, это происходит потому, что транзакция, управляемая контейнером JTA, завершается, когда завершается открытый метод getDishes() EJB. Субъекты JPA отделяются и сеанс закрывается, и уже невозможно более лениво загружать компоненты на странице JSF.

После некоторых исследований казалось, что наиболее распространенным решением для этого является JOIN FETCH. Поэтому я изменил свой запрос JPA на

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

И проблема решена. Я должен был добавить DISTINCT, так как без него запрос производит декартово произведение всех блюд и ингредиентов, ожидаемого, я полагаю.

Итак, наконец, вопрос: правильно ли я понял проблему и JOIN FETCH правильное решение?

+0

Я думаю, что это может сработать, просто удалив инициализацию в слое persistence: 'private List components;'. – Omar

+0

Я пробовал без инициализации, и хотя он не нужен, удаление его не решает проблему. –

ответ

3

Да, установка выборки соединения по запросу является наиболее подходящим способом.

Вы также можете использовать диаграммы сущностей, новую функцию в JPA 2.1, чтобы сделать выборку.

Причина этого в том, что транзакция начинается с вашего управляемого CDI компонента. JSF будет вызывать управляемый компонент, но вокруг него есть несколько оберток. Он пытается получить значения за пределами транзакции.

1

Для @Many типа отношений, по умолчанию FetchType является LAZY, что означает, что List<Ingredient> не будет загружена до 1-й раз это доступ.

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 решение выглядит хорошо.

Смежные вопросы