Каково решение проблемы N + 1 в спящем режиме?

Я понимаю, что проблема N + 1 заключается в том, что один запрос выполняется для извлечения N записей и N запросов для извлечения некоторых реляционных записей.

Но как его можно избежать в Hibernate?

Ответ 1

Предположим, что у нас есть класс-изготовитель со многими отношениями с контактом.

Мы решаем эту проблему, убедившись, что исходный запрос извлекает все данные, необходимые для загрузки необходимых нам объектов в их правильно инициализированном состоянии. Один из способов сделать это - использовать соединение для получения HQL. Мы используем HQL

"from Manufacturer manufacturer join fetch manufacturer.contact contact"

с помощью инструкции выборки. Это приводит к внутреннему соединению:

select MANUFACTURER.id from manufacturer and contact ... from 
MANUFACTURER inner join CONTACT on MANUFACTURER.CONTACT_ID=CONTACT.id

Используя запрос Criteria, мы можем получить тот же результат от

Criteria criteria = session.createCriteria(Manufacturer.class);
criteria.setFetchMode("contact", FetchMode.EAGER);

который создает SQL:

select MANUFACTURER.id from MANUFACTURER left outer join CONTACT on 
MANUFACTURER.CONTACT_ID=CONTACT.id where 1=1

в обоих случаях наш запрос возвращает список объектов производителя с инициализированным контактом. Необходимо выполнить только один запрос, чтобы вернуть все необходимые сведения о контакте и изготовителе.

для получения дополнительной информации здесь приведена ссылка на проблему и решение

Ответ 2

Нативное решение для 1 + N в спящем режиме называется:

20.1.5. Использование пакетной выборки

Используя выборку партии, Hibernate может загружать несколько неинициализированных прокси-серверов, если к одному прокси-серверу обращаются. Пакетная выборка - это оптимизация ленивой стратегии выбора выборки. Существует два способа настройки пакетной выборки: на уровне 1) и уровне набора 2...

Проверьте эти Q и A:

С аннотациями мы можем сделать это следующим образом:

A class уровень:

@Entity
@BatchSize(size=25)
@Table(...
public class MyEntity implements java.io.Serializable {...

A collection уровень:

@OneToMany(fetch = FetchType.LAZY...)
@BatchSize(size=25)
public Set<MyEntity> getMyColl() 

Ленивая загрузка и пакетная выборка вместе представляют собой оптимизацию, которая:

  • не требуется явная выборка в наших запросах
  • будет применяться к любому количеству ссылок, которые (лениво) затрагиваются после загрузки корневого объекта (в то время как явные выборки влияют только на эти имена в запросе)
  • будет решать проблему 1 + N с помощью коллекций (потому что только одна коллекция может быть извлечена с помощью корневого запроса) без необходимости дальнейшей обработки Чтобы получить корневые значения DISTINCT (проверьте: Criteria.DISTINCT_ROOT_ENTITY против Projections.distinct)