Статьи

Четыре решения для LazyInitializationException – Часть 2

Эта статья продолжается с первой части урока.

Загрузка коллекции с помощью Stateful EJB с PersistenceContextType.EXTENDED

Этот подход может быть применен только к приложениям, работающим со средами Full JEE: использовать EJB с PersistenceContextType.EXTENDED.

Проверьте код ниже, как будет выглядеть DAO:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<b>package com.ejb;
 
import javax.ejb.Stateful;
import javax.persistence.*;
 
import com.model.Person;
 
@Stateful
public class SystemDAOStateful {
 @PersistenceContext(unitName = 'LazyPU', type=PersistenceContextType.EXTENDED)
 private EntityManager entityManager;
 
 public Person findByName(String name) {
  Query query = entityManager.createQuery('select p from Person p where name = :name');
  query.setParameter('name', name);
 
  Person result = null;
  try {
   result = (Person) query.getSingleResult();
  } catch (NoResultException e) {
   // no result found
  }
 
  return result;
 }
}</b>
01
02
03
04
05
06
07
08
09
10
<b>public class DataMB {
 // other methods and attributes
 
 @EJB
 private SystemDAOStateful daoStateful;
 
 public Person getPersonByStatefulEJB() {
  return daoStateful.findByName('Mark M.');
 }
}</b>
1
2
3
4
5
6
7
8
<b><h:dataTable var='dog' value='#{dataMB.personByStatefulEJB.lazyDogs}'>
 <h:column>
  <f:facet name='header'>
   Dog name
  </f:facet>
  #{dog.name}
 </h:column>
</h:dataTable></b>

Плюсы и минусы этого подхода:

Pros

Cons

Контейнер будет контролировать транзакцию базы данных

Работает только для JEE

Модельные классы редактировать не нужно

Эффект N + 1 может произойти

Большое количество Stateful EJB может повлиять на память контейнера.

Этот подход может создать эффект N + 1, и Stateful EJB имеет свойство не удаляться / уничтожаться, пока не истек срок его сеанса или пока он не потерял свою ссылку.

Предупреждение . Не рекомендуется хранить ссылку на внедренный EJB-объект в объектах, которые остаются в пуле. JSF создаст пул ManagedBean для лучшей обработки пользовательских запросов; когда это необходимо, контейнер будет увеличивать или уменьшать количество ManagedBeans в пуле. В коде этого поста представьте, что если контейнер создает 100 экземпляров ManagedBeans в пуле, сервер будет хранить в памяти 100 Stateful EJB. Решением этой проблемы будет поиск JNDI в Stateful EJB.

Загрузить коллекцию с помощью Join Query

Это решение легко понять и применить.

Смотрите код ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
<b> public Person findByNameWithJoinFech(String name) {
  Query query = entityManager.createQuery('select p from Person p join fetch p.lazyDogs where p.name = :name');
  query.setParameter('name', name);
 
  Person result = null;
  try {
   result = (Person) query.getSingleResult();
  } catch (NoResultException e) {
   // no result found
  }
 
  return result;
 }</b>
1
2
3
<b> public Person getPersonByQuery() {
  return systemDAO.findByNameWithJoinFech('Mark M.');
 }</b>
1
2
3
4
5
6
7
8
<b> <h:dataTable var='dog' value='#{dataMB.personByQuery.lazyDogs}'>
  <h:column>
   <f:facet name='header'>
    Dog name
   </f:facet>
   #{dog.name}
  </h:column>
 </h:dataTable></b>

Плюсы и минусы этого подхода:

Pros

Cons

В базе данных будет запущен только один запрос

Для каждого доступного атрибута collection / lazy необходим один запрос

Модельные классы редактировать не нужно

Принесет только нужные данные

Эффект N + 1 не произойдет

Недостатком этого подхода является необходимость нового запроса для доступа к каждому атрибуту коллекции / ленивого класса модели. Если нам нужно запросить только собак-людей, нам потребуется конкретный запрос. Представьте, что нам нужно было бы запросить электронные письма о персоне, это был бы другой запрос.

Этот подход может быть применен к JSE и JEE.

EclipseLink и ленивая коллекция инициализации

Значения по умолчанию для отношений:

отношения

получать

@Один к одному

EAGER

@Один ко многим

LAZY

@ManyToOne

EAGER

@ManyToMany

LAZY

Но JPA Spec * говорит, что:

Стратегия EAGER – это требование времени выполнения провайдера постоянства, что данные должны извлекаться с нетерпением. Стратегия LAZY – это подсказка среде выполнения персистентного поставщика, что данные должны извлекаться лениво при первом обращении к ним. Реализация позволяет охотно получать данные, для которых указана подсказка стратегии LAZY . В частности, отложенная выборка может быть доступна только для базовых отображений, для которых используется доступ на основе свойств.

Как видно из приведенного выше текста, реализация JPA может игнорировать стратегию подсказок, если она этого хочет. EclipseLink имеет поведение с JEE и другое поведение с JSE. Вы можете увидеть каждое поведение здесь:

Мы можем найти в Интернете людей, которые говорят, что даже с ленивой коллекцией EclipseLink выполняет n + 1 запросы при загрузке сущности. И мы можем найти такое поведение пользователей с Glassfish и EJB.

Ниже вы увидите несколько советов по правильному использованию отложенной загрузки с EclipseLink:

* JSR-000220 Enterprise JavaBeans 3.0 Final Release (постоянство) 9.1.18 и будет повторять отношения JPA для выдр.

Конец!

На мой взгляд, лучшее решение – это запрос Join Fetch. Это зависит от вас, чтобы выбрать лучшее решение для вашего приложения .

Нажмите здесь, чтобы загрузить исходный код этого поста . Если вы хотите запустить код этого поста, вам нужно будет создать базу данных с именем LazyExceptionDB и модуль JBoss. К исходному коду прилагается модуль Postgres. Если вы хотите узнать, как настроить источник данных и модуль Postgres или MySQL, вы можете увидеть его здесь: Full WebApplication JSF EJB JPA JAAS .

Я надеюсь, что этот пост может помочь вам.

Если у вас есть какие-либо комментарии или сомнения, просто опубликуйте их.

До скорой встречи.

Ссылка: четыре решения для LazyInitializationException от нашего партнера JCG Хеберта Коэльо в блоге uaiHebert .