Автор оригинала: Vlad Mihalcea.
Вступление
В этой статье я собираюсь объяснить, что такое план выборки по умолчанию JPA и чем он отличается от плана выборки запроса при использовании FetchType
ассоциаций.
Тип выборки JPA
Ассоциацию JPA можно получить лениво или нетерпеливо. Стратегия извлечения управляется с помощью атрибута fetch
|/@ OneToMany , @OneToOne , @ManyToOne или @ManyToMany .
Атрибут fetch
может быть либо FetchType.ЛЕНИВЫЙ
или Тип выборки.НЕТЕРПЕЛИВЫЙ
. По умолчанию @OneToMany
и @ManyToMany
ассоциации используют Тип выборки.ЛЕНИВАЯ
стратегия, в то время как @OneToOne
и @ManyToOne
используют Тип выборки.Вместо этого СТРЕМИТЕСЬ
к стратегии.
Как я объяснил в этой статье , Тип выборки.НЕТЕРПЕЛИВАЯ
стратегия ужасна по умолчанию. Никогда в своей жизни я не видел хорошего варианта использования, который требовал бы ассоциации для использования FetchType.НЕТЕРПЕЛИВЫЙ
стратегия. Это связано с тем, что маловероятно, что каждый возможный вариант использования в бизнесе потребует извлечения данной ассоциации, и тот факт, что Hibernate не может переопределить тип FetchType.НЕТЕРПЕЛИВАЯ
стратегия с типом выборки.ЛЕНИВЫЙ
во время выполнения запроса.
План Выборки По Умолчанию
У каждой сущности есть план выборки по умолчанию, заданный стратегиями выборки, настроенными во время сопоставления. Например, следующая сущность Комментарий к сообщению
имеет ассоциацию сообщение
, которая использует тип выборки по умолчанию .НЕТЕРПЕЛИВАЯ
стратегия, данная @ManyToOne
аннотацией.
@Entity(name = "PostComment") @Table(name = "post_comment") public class PostComment { @Id private Long id; @ManyToOne private Post post; private String review; //Getters and setters omitted for brevity }
При извлечении Комментария к сообщению
сущности с помощью метода найти
:
PostComment comment = entityManager.find(PostComment.class, 1L);
Будет использоваться план выборки по умолчанию, и будет добавлено ЛЕВОЕ СОЕДИНЕНИЕ
, чтобы гарантировать, что сообщение
ассоциация будет извлечена с нетерпением:
SELECT pc.id AS id1_1_0_, pc.post_id AS post_id3_1_0_, pc.review AS review2_1_0_, p.id AS id1_0_1_, p.title AS title2_0_1_ FROM post_comment pc LEFT OUTER JOIN post p ON pc.post_id = p.id WHERE pc.id = 1
План выборки запроса
Однако, если вы извлекаете объект с помощью запроса JPQL:
PostComment comment = entityManager.createQuery(""" select pc from PostComment pc where pc.id = :id """, PostComment.class) .setParameter("id", 1L) .getSingleResult();
Затем план выборки по умолчанию переопределяется планом выборки запроса, поэтому выполняются два запроса вместо одного:
SELECT pc.id AS id1_1_, pc.post_id AS post_id3_1_, pc.review AS review2_1_ FROM post_comment pc WHERE pc.id = 1 SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 1
Запрос явно определяет план выборки, поэтому план выборки по умолчанию переопределяется. Однако ассоциация, которую необходимо быстро извлечь, все равно будет извлечена до возврата набора результатов запроса, и именно поэтому вторичные запросы выполняются для FetchType.НЕТЕРПЕЛИВЫЕ
ассоциации при выполнении JPQL, API критериев или собственного SQL-запроса, который извлек объекты.
Вот почему FetchType.НЕТЕРПЕЛИВЫЙ
– это опасное отображение. Это заставляет вас использовать JOIN FETCH
во всех ваших запросах, которые извлекают сущность, содержащую Тип выборки.НЕТЕРПЕЛИВЫЙ
ассоциация.
Вывод
План выборки JPA по умолчанию-это тот, который вы предоставляете при сопоставлении своих сущностей. Запросы переопределяют план выборки по умолчанию и предоставляют свой собственный план.
Однако, в то время как FetchType.ЛЕНИВЫЕ
ассоциации могут быть быстро извлечены во время выполнения запроса с помощью директивы JOIN FETCH
, FetchType.НЕТЕРПЕЛИВЫЕ
ассоциации не могут быть извлечены лениво, так как вторичные запросы всегда будут выполняться, чтобы гарантировать, что Тип выборки.НЕТЕРПЕЛИВЫЕ
ассоциации всегда инициализируются.