Стратегии загрузки коллекций в Hibernate

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

Для тех, кто не читал, повторюсь: отношениям один-ко-многим или многие-ко-многим между таблицами реляционной базы данных в объектном виде соответствуют свойства сущности типа List или Set, размеченные аннотациями @OneToMany или @ManyToMany. При работе с сущностями, которые содержат коллекции других сущностей, возникает проблема известная как «N+1 selects». Первый запрос выберет только корневые сущности, а каждая связанная коллекция будет загружена отдельным запросом. Таким образом ORM выполняет N+1 SQL запросов, где N — количество корневых сущностей в результирующей выборке запроса.

Итак, самая популярная реализация JPA, Hibernate предоставляет множество способов управления загрузкой отношений один-ко-многим и многие-ко-многим.

FetchMode в Hibernate говорит как мы хотим, чтоб связанные сущности или коллекции были загружены:
— SELECT — используя по дополнительному SQL запросу на коллекцию,
— JOIN — в одном запросе с корневой сущностью, используя SQL оператор JOIN,
— SUBSELECT— в дополнительном запросе, используя SUBSELECT.

Мы также можем влиять на стратегию загрузки связанных коллекций при помощи аннотации @BatchSize (или атрибут batch-size в XML), которая устанавливает количество коллекций, которые будут загружаться в одном запросе.

Пример

Продолжим рассматривать пример из предыдущей части статьи, в котором сущность Book владеет отношениями многие-ко-многим с сущностями Author и Category. Дополним этот пример, добавив аннотации из пакета org.hibernate.annotations, которые не являются частью JPA. Пример целиком доступен на Github.

@Entity
public class Book extends AbstractBook {

    @ManyToMany(fetch = FetchType.EAGER)
    private List<Author> authors = new ArrayList<>();

    @ManyToMany
    private List<Category> categories = new ArrayList<>();

    /*...*/
}

@Entity
public class BookFetchModeSelect extends AbstractBook {

    @ManyToMany(fetch = FetchType.EAGER)
    @Fetch(FetchMode.SELECT)
    private List<Author> authors = new ArrayList<>();

    @ManyToMany
    @Fetch(FetchMode.SELECT)
    private List<Category> categories = new ArrayList<>();

    /*...*/
}

@Entity
public class BookFetchModeJoin extends AbstractBook {

    @ManyToMany(fetch = FetchType.EAGER)
    @Fetch(FetchMode.JOIN)
    private List<Author> authors = new ArrayList<>();

    @ManyToMany
    @Fetch(FetchMode.JOIN)
    private List<Category> categories = new ArrayList<>();
    
    /*...*/
}

@Entity
public class BookFetchModeSubselect extends AbstractBook {

    @ManyToMany(fetch = FetchType.EAGER)
    @Fetch(FetchMode.SUBSELECT)
    private List<Author> authors = new ArrayList<>();

    @ManyToMany
    @Fetch(FetchMode.SUBSELECT)
    private List<Category> categories = new ArrayList<>();

    /*...*/
}

@Entity
public class BookBatchSize extends AbstractBook {

    @ManyToMany(fetch = FetchType.EAGER)
    // Явное указание FetchMode.SELECT необходимо
    // так как в Criteria API EAGER ассоциации по умолчанию загружаются с
    @Fetch(FetchMode.SELECT)
    @BatchSize(size = 2)
    private List<Author> authors = new ArrayList<>();

    @ManyToMany
    @Fetch(FetchMode.SELECT)
    @BatchSize(size = 2)
    private List<Category> categories = new ArrayList<>();
    
    /*...*/
}

Для более наглядной демонстрации отличий режимов загрузки необходимо добавить больше тестовых данных:

Session session = sessionFactory.getCurrentSession();

Category softwareDevelopment = new Category();
softwareDevelopment.setName("Software development");
session.persist(softwareDevelopment);

Category systemDesign = new Category();
systemDesign.setName("System design");
session.persist(systemDesign);

Author martinFowler = new Author();
martinFowler.setFullName("Martin Fowler");
session.persist(martinFowler);

AbstractBook poeaa = bookSupplier.get();
poeaa.setIsbn("007-6092019909");
poeaa.setTitle("Patterns of Enterprise Application Architecture");
poeaa.setPublicationDate(Date.from(Instant.parse("2002-11-15T00:00:00.00Z")));
poeaa.setAuthors(asList(martinFowler));
poeaa.setCategories(asList(softwareDevelopment, systemDesign));
session.persist(poeaa);

Author gregorHohpe = new Author();
gregorHohpe.setFullName("Gregor Hohpe");
session.persist(gregorHohpe);

Author bobbyWoolf = new Author();
bobbyWoolf.setFullName("Bobby Woolf");
session.persist(bobbyWoolf);

AbstractBook eip = bookSupplier.get();
eip.setIsbn("978-0321200686");
eip.setTitle("Enterprise Integration Patterns");
eip.setPublicationDate(Date.from(Instant.parse("2003-10-20T00:00:00.00Z")));
eip.setAuthors(asList(gregorHohpe, bobbyWoolf));
eip.setCategories(asList(softwareDevelopment, systemDesign));
session.persist(eip);

Category objectOrientedSoftwareDesign = new Category();
objectOrientedSoftwareDesign.setName("Object-Oriented Software Design");
session.persist(objectOrientedSoftwareDesign);

Author ericEvans = new Author();
ericEvans.setFullName("Eric Evans");
session.persist(ericEvans);

AbstractBook ddd = bookSupplier.get();
ddd.setIsbn("860-1404361814");
ddd.setTitle("Domain-Driven Design: Tackling Complexity in the Heart of Software");
ddd.setPublicationDate(Date.from(Instant.parse("2003-08-01T00:00:00.00Z")));
ddd.setAuthors(asList(ericEvans));
ddd.setCategories(asList(softwareDevelopment, systemDesign, objectOrientedSoftwareDesign));
session.persist(ddd);

Category networkingCloudComputing = new Category();
networkingCloudComputing.setName("Networking & Cloud Computing");
session.persist(networkingCloudComputing);

Category databasesBigData = new Category();
databasesBigData.setName("Databases & Big Data");
session.persist(databasesBigData);

Author pramodSadalage = new Author();
pramodSadalage.setFullName("Pramod J. Sadalage");
session.persist(pramodSadalage);

AbstractBook nosql = bookSupplier.get();
nosql.setIsbn("978-0321826626");
nosql.setTitle("NoSQL Distilled: A Brief Guide to the Emerging World of Polyglot Persistence");
nosql.setPublicationDate(Date.from(Instant.parse("2012-08-18T00:00:00.00Z")));
nosql.setAuthors(asList(pramodSadalage, martinFowler));
nosql.setCategories(asList(networkingCloudComputing, databasesBigData));
session.persist(nosql);

В тестах используется Hibernate 5.2.0.Final.

HQL запрос и FetchMode по умолчанию

Когда аннотация @Fetch не добавлена, HQL и Hibernate Criteria запросы ведут себя по-разному. В случае использования HQL запроса, по умолчанию используется FetchMode.SELECT для связанных коллекций с любым типом загрузки (EAGER и LAZY).

List books = getCurrentSession().createQuery("select b from Book b").list(); 
assertEquals(4, books.size());

Сгенерированный SQL:

select
        book0_.id as id1_1_,
        book0_.isbn as isbn2_1_,
        book0_.publicationDate as publicat3_1_,
        book0_.title as title4_1_
    from
        Book book0_

select
    authors0_.Book_id as Book_id1_2_0_,
    authors0_.authors_id as authors_2_2_0_,
    author1_.id as id1_0_1_,
    author1_.fullName as fullName2_0_1_
from
    Book_Author authors0_
inner join
    Author author1_
        on authors0_.authors_id=author1_.id
where
    authors0_.Book_id=?

select
    authors0_.Book_id as Book_id1_2_0_,
    authors0_.authors_id as authors_2_2_0_,
    author1_.id as id1_0_1_,
    author1_.fullName as fullName2_0_1_
from
    Book_Author authors0_
inner join
    Author author1_
        on authors0_.authors_id=author1_.id
where
    authors0_.Book_id=?

select
    authors0_.Book_id as Book_id1_2_0_,
    authors0_.authors_id as authors_2_2_0_,
    author1_.id as id1_0_1_,
    author1_.fullName as fullName2_0_1_
from
    Book_Author authors0_
inner join
    Author author1_
        on authors0_.authors_id=author1_.id
where
    authors0_.Book_id=?

select
    authors0_.Book_id as Book_id1_2_0_,
    authors0_.authors_id as authors_2_2_0_,
    author1_.id as id1_0_1_,
    author1_.fullName as fullName2_0_1_
from
    Book_Author authors0_
inner join
    Author author1_
        on authors0_.authors_id=author1_.id
where
    authors0_.Book_id=?

Hibernate Criteria запрос и FetchMode по умолчанию

Когда аннотация @Fetch не добавлена, в Hibernate Criteria запросах по умолчанию используется FetchMode.JOIN для связанных коллекций с типом загрузки EAGER и tchMode.SELECT для связанных коллекций с типом загрузки LAZY.

Коллекции с типом загрузки EAGER будут загружены в одном SQL запросе с корневой сущностью. Результатом запроса будет декартово произведение (cartesian product). Вместо 4 элементов в результирующей выборке, запрос вернет 6, потому что книги «Enterprise Integration Patterns» и «NoSQL» имеют двух авторов и будут дважды встречаться в результатах запроса.

Стоит отметить, что начиная с версии Hibernate 5.2 метод createCriteria класса org.hibernate.Session, который создает экземпляр org.hibernate.Criteria, считает устаревшим и помечен аннотацией @Deprecated. Вместо этого в Javadoc рекомендуется использовать JPA Criteria.

List books = getCurrentSession().createCriteria(Book.class).list(); 
assertEquals(6, books.size());

Сгенерированный SQL:

select
    this_.id as id1_1_1_,
    this_.isbn as isbn2_1_1_,
    this_.publicationDate as publicat3_1_1_,
    this_.title as title4_1_1_,
    authors2_.Book_id as Book_id1_2_3_,
    author3_.id as authors_2_2_3_,
    author3_.id as id1_0_0_,
    author3_.fullName as fullName2_0_0_ 
from
    Book this_ 
left outer join
    Book_Author authors2_ 
        on this_.id=authors2_.Book_id 
left outer join
    Author author3_ 
        on authors2_.authors_id=author3_.id

HQL и Hibernate Criteria запросы и FetchMode.SELECT

С режимом загрузки FetchMode.SELECT первый запрос выберет только корневые сущности, а каждая связанная коллекция будет загружена отдельным запросом. Так как для данного примера мы сохранили 4 сущности, то запросов будет 5. Один для загрузки сущностей Book и по одному для каждой из 4 сущностей Book для загрузки списка сущностей Author. Список сущностей Category имеет тип загрузки по умолчанию (LAZY для коллекций) и будет загружен отдельным запросом при первом обращении в коде.

List books = getCurrentSession().createQuery("select b from BookFetchModeSelect b").list();
assertEquals(4, books.size());

При режиме загрузки FetchMode.SELECT HQL и Hibernate Criteria запросы ведут себя одинаково.

List books = getCurrentSession().createCriteria(BookFetchModeSelect.class).list(); 
assertEquals(4, books.size());

Сгенерированный SQL:

select
    bookfetchm0_.id as id1_10_,
    bookfetchm0_.isbn as isbn2_10_,
    bookfetchm0_.publicationDate as publicat3_10_,
    bookfetchm0_.title as title4_10_
from
    BookFetchModeSelect bookfetchm0_

select
    authors0_.BookFetchModeSelect_id as BookFetc1_11_0_,
    authors0_.authors_id as authors_2_11_0_,
    author1_.id as id1_0_1_,
    author1_.fullName as fullName2_0_1_
from
    BookFetchModeSelect_Author authors0_
inner join
    Author author1_
        on authors0_.authors_id=author1_.id
where
    authors0_.BookFetchModeSelect_id=?
 
select
    authors0_.BookFetchModeSelect_id as BookFetc1_11_0_,
    authors0_.authors_id as authors_2_11_0_,
    author1_.id as id1_0_1_,
    author1_.fullName as fullName2_0_1_
from
    BookFetchModeSelect_Author authors0_
inner join
    Author author1_
        on authors0_.authors_id=author1_.id
where
    authors0_.BookFetchModeSelect_id=?

select
    authors0_.BookFetchModeSelect_id as BookFetc1_11_0_,
    authors0_.authors_id as authors_2_11_0_,
    author1_.id as id1_0_1_,
    author1_.fullName as fullName2_0_1_
from
    BookFetchModeSelect_Author authors0_
inner join
    Author author1_
        on authors0_.authors_id=author1_.id
where
    authors0_.BookFetchModeSelect_id=?
 
select
    authors0_.BookFetchModeSelect_id as BookFetc1_11_0_,
    authors0_.authors_id as authors_2_11_0_,
    author1_.id as id1_0_1_,
    author1_.fullName as fullName2_0_1_
from
    BookFetchModeSelect_Author authors0_
inner join
    Author author1_
        on authors0_.authors_id=author1_.id
where
    authors0_.BookFetchModeSelect_id=?<pre>

<h2>HQL и Hibernate Criteria запросы и FetchMode.SUBSELECT</h2>

При режиме загрузки <code>FetchMode.SUBSELECT</code> будет выполнено 2 SQL запроса. Первый загрузит корневые сущности, а второй - связанные коллекции для всех корневых сущностей из результатов первого SQL запроса, используя подзапрос.

<pre>List books = getCurrentSession().createQuery("select b from BookFetchModeSubselect b").list();
assertEquals(4, books.size());

HQL и Hibernate Criteria запросы ведут себя одинаково и при режиме загрузки FetchMode.SUBSELECT.

List books = getCurrentSession().createCriteria(BookFetchModeSubselect.class).list();
assertEquals(4, books.size());

Сгенерированный SQL:

select
    bookfetchm0_.id as id1_13_,
    bookfetchm0_.isbn as isbn2_13_,
    bookfetchm0_.publicationDate as publicat3_13_,
    bookfetchm0_.title as title4_13_
from
    BookFetchModeSubselect bookfetchm0_

select
    authors0_.BookFetchModeSubselect_id as BookFetc1_14_1_,
    authors0_.authors_id as authors_2_14_1_,
    author1_.id as id1_0_0_,
    author1_.fullName as fullName2_0_0_
from
    BookFetchModeSubselect_Author authors0_
inner join
    Author author1_
        on authors0_.authors_id=author1_.id
where
    authors0_.BookFetchModeSubselect_id in (
        select
            bookfetchm0_.id
        from
            BookFetchModeSubselect bookfetchm0_
    )

HQL запрос и FetchMode.JOIN

Поведение HQL запросов при режиме загрузке FetchMode.JOIN, на первый взгляд, немного неожиданное. Вместо того, чтобы загрузить связанные коллекции, помеченные аннотацией @Fetch(FetchMode.JOIN), в одном запросе с корневыми сущностями, используя SQL оператор JOIN, HQL запрос транслируется в несколько SQL запросов по типа FetchMode.SELECT. Но в отличии от FetchMode.SELECT, при FetchMode.JOIN будет игнорироваться указанный тип загрузки (LAZY и EAGER) и все коллекции будут загружены сразу, а не при первом обращении в коде (поведение соответствующее типу EAGER).

Таким образом в следующем примере будет выполнено 8 SQL запросов. Один запрос для загрузки корневых сущностей Book и для каждой из 4 корневых сущностей из результатов первого SQL запроса по одному запросу для загрузки списка сущностей Author и по одному запросу для загрузки списка сущностей Category.

List books = getCurrentSession().createQuery("select b from BookFetchModeJoin b").list();
assertEquals(4, books.size());

Сгенерированный SQL:

select
    bookfetchm0_.id as id1_7_,
    bookfetchm0_.isbn as isbn2_7_,
    bookfetchm0_.publicationDate as publicat3_7_,
    bookfetchm0_.title as title4_7_
from
    BookFetchModeJoin bookfetchm0_

select
    categories0_.BookFetchModeJoin_id as BookFetc1_9_0_,
    categories0_.categories_id as categori2_9_0_,
    category1_.id as id1_16_1_,
    category1_.description as descript2_16_1_,
    category1_.name as name3_16_1_
from
    BookFetchModeJoin_Category categories0_
inner join
    Category category1_
        on categories0_.categories_id=category1_.id
where
    categories0_.BookFetchModeJoin_id=?

select
    authors0_.BookFetchModeJoin_id as BookFetc1_8_0_,
    authors0_.authors_id as authors_2_8_0_,
    author1_.id as id1_0_1_,
    author1_.fullName as fullName2_0_1_
from
    BookFetchModeJoin_Author authors0_
inner join
    Author author1_
        on authors0_.authors_id=author1_.id
where
    authors0_.BookFetchModeJoin_id=?

select
    categories0_.BookFetchModeJoin_id as BookFetc1_9_0_,
    categories0_.categories_id as categori2_9_0_,
    category1_.id as id1_16_1_,
    category1_.description as descript2_16_1_,
    category1_.name as name3_16_1_
from
    BookFetchModeJoin_Category categories0_
inner join
    Category category1_
        on categories0_.categories_id=category1_.id
where
    categories0_.BookFetchModeJoin_id=?

select
    authors0_.BookFetchModeJoin_id as BookFetc1_8_0_,
    authors0_.authors_id as authors_2_8_0_,
    author1_.id as id1_0_1_,
    author1_.fullName as fullName2_0_1_
from
    BookFetchModeJoin_Author authors0_
inner join
    Author author1_
        on authors0_.authors_id=author1_.id
where
    authors0_.BookFetchModeJoin_id=?

select
    categories0_.BookFetchModeJoin_id as BookFetc1_9_0_,
    categories0_.categories_id as categori2_9_0_,
    category1_.id as id1_16_1_,
    category1_.description as descript2_16_1_,
    category1_.name as name3_16_1_
from
    BookFetchModeJoin_Category categories0_
inner join
    Category category1_
        on categories0_.categories_id=category1_.id
where
    categories0_.BookFetchModeJoin_id=?

select
    authors0_.BookFetchModeJoin_id as BookFetc1_8_0_,
    authors0_.authors_id as authors_2_8_0_,
    author1_.id as id1_0_1_,
    author1_.fullName as fullName2_0_1_
from
    BookFetchModeJoin_Author authors0_
inner join
    Author author1_
        on authors0_.authors_id=author1_.id
where
    authors0_.BookFetchModeJoin_id=?

select
    categories0_.BookFetchModeJoin_id as BookFetc1_9_0_,
    categories0_.categories_id as categori2_9_0_,
    category1_.id as id1_16_1_,
    category1_.description as descript2_16_1_,
    category1_.name as name3_16_1_
from
    BookFetchModeJoin_Category categories0_
inner join
    Category category1_
        on categories0_.categories_id=category1_.id
where
    categories0_.BookFetchModeJoin_id=?

select
    authors0_.BookFetchModeJoin_id as BookFetc1_8_0_,
    authors0_.authors_id as authors_2_8_0_,
    author1_.id as id1_0_1_,
    author1_.fullName as fullName2_0_1_
from
    BookFetchModeJoin_Author authors0_
inner join
    Author author1_
        on authors0_.authors_id=author1_.id
where
    authors0_.BookFetchModeJoin_id=?

HQL запрос с «join fetch»

Чтобы корневые сущности со связанными коллекциями были загружены в одном SQL запросе, используйте оператор JOIN FETCH. Результатом запроса будет декартово произведение. Вместо 4 элементов в результирующей выборке, запрос вернет 6, потому что книги «Enterprise Integration Patterns» и «NoSQL» имеют двух авторов и будут дважды встречаться в результатах запроса. Коллекции, которые не были присоединены оператором JOIN FETCH, будут загружены согласно типу и режиму загрузки.

В данном примере будет выполнено по дополнительному запросу на каждую корневую сущность из списка результатов для загрузки списка сущностей Category.

List books = getCurrentSession().createQuery("select b from BookFetchModeJoin b join fetch b.authors a").list(); 
assertEquals(6, books.size());

Сгенерированный SQL:

select
    bookfetchm0_.id as id1_7_0_,
    author2_.id as id1_0_1_,
    bookfetchm0_.isbn as isbn2_7_0_,
    bookfetchm0_.publicationDate as publicat3_7_0_,
    bookfetchm0_.title as title4_7_0_,
    author2_.fullName as fullName2_0_1_,
    authors1_.BookFetchModeJoin_id as BookFetc1_8_0__,
    authors1_.authors_id as authors_2_8_0__
from
    BookFetchModeJoin bookfetchm0_
inner join
    BookFetchModeJoin_Author authors1_
        on bookfetchm0_.id=authors1_.BookFetchModeJoin_id
inner join
    Author author2_
        on authors1_.authors_id=author2_.id

select
    categories0_.BookFetchModeJoin_id as BookFetc1_9_0_,
    categories0_.categories_id as categori2_9_0_,
    category1_.id as id1_16_1_,
    category1_.description as descript2_16_1_,
    category1_.name as name3_16_1_
from
    BookFetchModeJoin_Category categories0_
inner join
    Category category1_
        on categories0_.categories_id=category1_.id
where
    categories0_.BookFetchModeJoin_id=?

select
    categories0_.BookFetchModeJoin_id as BookFetc1_9_0_,
    categories0_.categories_id as categori2_9_0_,
    category1_.id as id1_16_1_,
    category1_.description as descript2_16_1_,
    category1_.name as name3_16_1_
from
    BookFetchModeJoin_Category categories0_
inner join
    Category category1_
        on categories0_.categories_id=category1_.id
where
    categories0_.BookFetchModeJoin_id=?

select
    categories0_.BookFetchModeJoin_id as BookFetc1_9_0_,
    categories0_.categories_id as categori2_9_0_,
    category1_.id as id1_16_1_,
    category1_.description as descript2_16_1_,
    category1_.name as name3_16_1_
from
    BookFetchModeJoin_Category categories0_
inner join
    Category category1_
        on categories0_.categories_id=category1_.id
where
    categories0_.BookFetchModeJoin_id=?

select
    categories0_.BookFetchModeJoin_id as BookFetc1_9_0_,
    categories0_.categories_id as categori2_9_0_,
    category1_.id as id1_16_1_,
    category1_.description as descript2_16_1_,
    category1_.name as name3_16_1_
from
    BookFetchModeJoin_Category categories0_
inner join
    Category category1_
        on categories0_.categories_id=category1_.id
where
    categories0_.BookFetchModeJoin_id=?

Hibernate Criteria запрос и FetchMode.JOIN

В Hibernate Criteria запросах связанные коллекции с режимом загрузки FetchMode.JOIN будут загружены сразу (тип загрузки EAGER) и в одном запросе с корневыми сущностями при помощи SQL оператора JOIN. Как уже рассматривалось в прошлой статье, только одна коллекций, которая загружается со стратегией JOIN может быть типа java.util.List, остальные коллекции, которые загружаются стратегией JOIN должны быть типа java.util.Set, иначе будет выброшено исключение org.hibernate.loader.MultipleBagFetchException.

List books = getCurrentSession().createCriteria(BookFetchModeJoin.class).list();

Будет выброшено исключение:
org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags

HQL и Hibernate Criteria запросы и FetchMode.SELECT с @BatchSize

@BatchSize устанавливает количество коллекций, которые должны быть загружены в одном SQL запросе. Если результат запроса содержит 4 сущности, каждая из которых имеет по связанной коллекции, при режим загрузки SELECT будет выполнено 5 запросов. Один запрос для загрузки всех корневых сущностей и по одному для загрузки связанной коллекции каждой из 4 корневых сущностей. @BatchSize(size = 2) указывает ORM загружать по 2 связанные коллекции в одном запросе. Таким образом, всего будет выполнено 3 запроса вместо 5. Один запрос для загрузки всех корневых сущностей и еще 2 запроса, каждый из которых загрузит по 2 связанные коллекции для 2 корневых сущностей.

List books = getCurrentSession().createQuery("select b from BookBatchSize b").list();
assertEquals(4, books.size());

Как уже отмечалось ранее, при режиме загрузки FetchMode.SELECT HQL и Hibernate Criteria запросы ведут себя одинаково. При использовании @BatchSize поведение также будет одинаковым.

List books = getCurrentSession().createCriteria(BookBatchSize.class).list(); 
assertEquals(4, books.size());

Сгенерированный SQL:

select
    bookbatchs0_.id as id1_4_,
    bookbatchs0_.isbn as isbn2_4_,
    bookbatchs0_.publicationDate as publicat3_4_,
    bookbatchs0_.title as title4_4_
from
    BookBatchSize bookbatchs0_

select
    authors0_.BookBatchSize_id as BookBatc1_5_1_,
    authors0_.authors_id as authors_2_5_1_,
    author1_.id as id1_0_0_,
    author1_.fullName as fullName2_0_0_
from
    BookBatchSize_Author authors0_
inner join
    Author author1_
        on authors0_.authors_id=author1_.id
where
    authors0_.BookBatchSize_id in (
        ?, ?
    )

select
    authors0_.BookBatchSize_id as BookBatc1_5_1_,
    authors0_.authors_id as authors_2_5_1_,
    author1_.id as id1_0_0_,
    author1_.fullName as fullName2_0_0_
from
    BookBatchSize_Author authors0_
inner join
    Author author1_
        on authors0_.authors_id=author1_.id
where
    authors0_.BookBatchSize_id in (
        ?, ?
    )

Выводы

Существует немало нюансов, связанных со стратегиями загрузки связанных коллекций в JPA и Hibernate, многие из которых были рассмотрены в этой и предыдущей статьях.

Не стоит использовать одну выбранную стратегию загрузки повсюду в приложении. Каждый случай надо проанализировать индивидуально и выбрать оптимальную стратегию загрузки. Как это часто бывает, в большинстве случаев стратегия загрузки по умолчанию будет оптимальным вариантом. Если приложение испытывает проблемы с производительностью, которые вызваны неправильным выбором стратегии загрузки, для анализа пригодятся следующие свойства Hibernate конфигурации: hibernate.show_sql=true, hibernate.format_sql=true и hibernate.use_sql_comments=true.

Похожие статьи:
У Бучі загинули щонайменше 400 українців. Польща та країни Балтії готують транспортну блокаду росії. DOU публікує короткий дайджест...
Онлайн курсы Android программирования от U-Rise. С Бесплатной Неделей обучения и гарантией результата. Первая онлайн школа...
Все началось давно, в начале двухтысячных, когда я, оказывая услуги по удаленной настройке Linux/FreeBSD серверов разным...
У свіжому дайджесті DOU News обговорюємо повторне оновлення даних в «Резерв+», інструменти GitHub та Grammarly, багато новин...
Меня зовут Станислав Малкин, я технический специалист. В разработке уже более 10 лет, а в данный момент DevOps...
Яндекс.Метрика