Рубрики
Без рубрики

График сущностей JPA

Узнайте, как переопределить план выборки по умолчанию, используя график сущностей JPA, который можно построить декларативно или программно.

Автор оригинала: 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 , как показано в следующем примере:

EntityGraph postCommentGraph = 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 .