Каково решение проблемы 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 в спящем режиме называется:
Используя выборку партии, 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)