Автор оригинала: Vlad Mihalcea.
Вступление
В этой статье я собираюсь объяснить, как вы можете получить ассоциацию сущностей с помощью графа сущностей JPA и как вы можете построить ее декларативно или программно.
Тип выборки JPA
Ассоциацию JPA можно получить лениво или нетерпеливо. Стратегия извлечения управляется с помощью атрибута fetch |/@ OneToMany , @OneToOne , @ManyToOne или @ManyToMany .
Атрибут fetch может быть либо FetchType.ЛЕНИВЫЙ или Тип выборки.НЕТЕРПЕЛИВЫЙ . По умолчанию @OneToMany и @ManyToMany ассоциации используют Тип выборки.ЛЕНИВАЯ стратегия, в то время как @OneToOne и @ManyToOne используют Тип выборки.Вместо этого СТРЕМИТЕСЬ к стратегии.
Как я объяснил в этой статье , Тип выборки.НЕТЕРПЕЛИВАЯ стратегия ужасна по умолчанию. Никогда в своей жизни я не видел хорошего варианта использования, который требовал бы ассоциации для использования FetchType.НЕТЕРПЕЛИВЫЙ стратегия. Это связано с тем, что маловероятно, что каждый возможный вариант использования в бизнесе потребует извлечения данной ассоциации, и тот факт, что Hibernate не может переопределить тип FetchType.НЕТЕРПЕЛИВАЯ стратегия с типом выборки.ЛЕНИВЫЙ во время выполнения запроса.
План Выборки По Умолчанию
Как я объяснил в этой статье , у каждой сущности есть план выборки по умолчанию, который определяется во время сопоставления сущностей и указывает Hibernate, как извлекать ассоциации сущностей.
По умолчанию @ManyToOne и @OneToOne ассоциации используют Тип выборки.НЕТЕРПЕЛИВАЯ стратегия, которая является ужасным выбором с точки зрения производительности. Поэтому по этой причине рекомендуется установить все @ManyToOne и @OneToOne ассоциации, чтобы подать в суд на Тип выборки.Стратегия LAZY , как в следующем примере:
@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {
@Id
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
private Post post;
private String review;
//Getters and setters omitted for brevity
}
При извлечении Комментария к сообщению сущности с помощью метода найти :
PostComment comment = entityManager.find(PostComment.class, 1L);
Hibernate выполняет следующий SQL-запрос:
SELECT pc.id AS id1_1_0_,
pc.post_id AS post_id3_1_0_,
pc.review AS review2_1_0_
FROM post_comment pc
WHERE pc.id = 1
Ассоциация post извлекается как Прокси , у которого есть только идентификатор , установленный столбцом post_id внешнего ключа, который был загружен вышеупомянутым SQL-запросом.
При доступе к любому свойству, не являющемуся идентификатором post Прокси-сервера:
LOGGER.info("The comment post title is '{}'", comment.getPost().getTitle());
Выполняется вторичный SQL-запрос, который извлекает сущность Post по требованию:
SELECT p.id AS id1_0_0_,
p.title AS title2_0_0_
FROM post p
WHERE p.id = 1
-- The comment post title is 'High-Performance Java Persistence, part 1'
Переопределение плана выборки по умолчанию
Если мы хотим переопределить план выборки по умолчанию и охотно извлекать ассоциацию post во время выполнения запроса, мы можем использовать запрос JPQL, который предписывает Hibernate извлекать ленивую ассоциацию с помощью предложения FETCH JOIN:
PostComment comment = entityManager.createQuery("""
select pc
from PostComment pc
left join fetch pc.post
where pc.id = :id
""", PostComment.class)
.setParameter("id", 1L)
.getSingleResult();
LOGGER.info("The comment post title is '{}'", comment.getPost().getTitle());
Затем план выборки по умолчанию будет переопределен, и ассоциация post будет извлечена с нетерпением:
SELECT pc.id AS id1_1_0_,
p.id AS id1_0_1_,
pc.post_id AS post_id3_1_0_,
pc.review AS review2_1_0_,
p.title AS title2_0_1_
FROM post_comment pc
LEFT JOIN post p ON pc.post_id = p.id
WHERE pc.id = 1
Декларативный граф сущностей JPA
План выборки по умолчанию также может быть переопределен с помощью диаграммы сущностей JPA. Например, мы могли бы определить конкретный план выборки, используя следующую аннотацию JPA @EntityGraph :
@Entity(name = "PostComment")
@Table(name = "post_comment")
@NamedEntityGraph(
name = "PostComment.post",
attributeNodes = @NamedAttributeNode("post")
)
public class PostComment {
//Code omitted for brevity
}
С помощью графика Post Comment.post Сущности теперь мы можем загрузить Post Comment сущность вместе с связанной с ней записью сущностью, например:
PostComment comment = entityManager.find(
PostComment.class,
1L,
Collections.singletonMap(
"javax.persistence.loadgraph",
entityManager.getEntityGraph("PostComment.post")
)
);
И при выполнении вышеупомянутого метода find Hibernate генерирует следующий запрос SQL SELECT:
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
Если вы используете Spring, то вы можете ссылаться на JPAEntityGraph в методе репозитория, используя аннотацию @EntityGraph :
@Repository
public interface PostCommentRepository
extends CrudRepository {
@EntityGraph(
value = "PostComment.post",
type = EntityGraphType.LOAD
)
PostComment findById(Long id);
}
Программный график сущностей JPA
Если вам не нравятся аннотации, вы также можете создать JPAEntityGraph программно, используя метод createEntityGraph JPA EntityManager , как показано в следующем примере:
EntityGraphpostCommentGraph = entityManager .createEntityGraph(PostComment.class); postCommentGraph.addAttributeNodes("post"); PostComment comment = entityManager.find( PostComment.class, 1L, Collections.singletonMap( "javax.persistence.loadgraph", postCommentGraph ) );
Потрясающе, правда?
Вывод
В этой статье вы узнали, как работает план выборки по умолчанию и как его можно переопределить с помощью запроса JPQL или графика сущностей JPA.
Граф сущностей JPA может быть построен декларативно с использованием аннотации JPA @NamedEntityGraph или программно с помощью метода createEntityGraph .