Автор оригинала: 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.НЕТЕРПЕЛИВЫЕ ассоциации не могут быть извлечены лениво, так как вторичные запросы всегда будут выполняться, чтобы гарантировать, что Тип выборки.НЕТЕРПЕЛИВЫЕ ассоциации всегда инициализируются.