Автор оригинала: 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
.