38. Что такое Entity Graph? Как и для чего его использовать?

38. Что такое Entity Graph? Как и для чего его использовать?

UNKNOWN

В  JPA  2.1  введен  EntityGraph  как  более  сложный  способ  решения  проблем  с производительностью при загрузке данных из БД. Он позволяет определить шаблон путем  группировки  связанных  полей,  которые  мы  хотим  получить,  и  позволяет  нам выбирать тип графа во время выполнения. 

До  JPA  2.0  для  загрузки  связанных  сущностей  и  коллекций  мы  обычно использовали FetchType.LAZY и FetchType.EAGER в качестве стратегий извлечения. 

Это указывало Hibernate, извлекать из БД связанные сущности и коллекции или нет. К сожалению, эта конфигурация статична и не позволяет переключаться между этими двумя стратегиями в рантайме.

Основная цель JPA Entity Graph - улучшить производительность в рантайме при загрузке базовых полей сущности и связанных сущностей и коллекций. Вкратце, Hibernate загружает весь граф в одном SELECT-запросе, то есть все указанные связи от нужной нам сущности:

@Entity

public class Comment { 

    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)

    private Long id;

    private String reply;

    @ManyToOne(fetch = FetchType.LAZY)

    @JoinColumn

    private Post post;

    @ManyToOne(fetch = FetchType.LAZY)

    @JoinColumn

    private User user;

    //...

}

@Entity

public class User {

    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)

    private Long id;

    private String name;

    private String email;

    //...

}

@NamedEntityGraph(

  name = \"post-entity-graph-with-comment-users\",

  attributeNodes = {  87

    @NamedAttributeNode(\"subject\"),

    @NamedAttributeNode(\"user\"),

    @NamedAttributeNode(value = \"comments\", subgraph = \"comments-subgraph\"),

  },

  subgraphs = {

    @NamedSubgraph(

      name = \"comments-subgraph\",

      attributeNodes = {

        @NamedAttributeNode(\"user\")

      }

    )

  }

)

@Entity

public class Post {

 

    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)

    private Long id;

    private String subject;

    @OneToMany(mappedBy = \"post\")

    private List<Comment> comments = new ArrayList<>();

 

    @ManyToOne(fetch = FetchType.LAZY)

    @JoinColumn

    private User user;

     

    //...

}

В данном примере мы создали EntityGraph и при его помощи хотим, чтобы при загрузке сущности Post загружались поля subject, user, comments. Также мы захотели, чтобы у каждой сущности comments загружалось поле user, для чего мы использовали subgraphs.

EntityGraph  можно  определить  и  без  помощи  аннотаций,  а  используя  entityManager из JPA API:

EntityGraph<Post> entityGraph = entityManager.createEntityGraph(Post.class);

entityGraph.addAttributeNodes(\"subject\");

entityGraph.addAttributeNodes(\"user\");

entityGraph.addSubgraph(\"comments\").addAttributeNodes(\"user\");

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

  • fetchgraph - из базы данных извлекаются только указанные в графе атрибуты. Поскольку мы используем Hibernate, мы можем заметить, что в отличие от спецификаций JPA, атрибуты, статически настроенные как EAGER, также загружаются.
  • loadgraph - в дополнение к указанным в графе атрибутам, также извлекаются атрибуты, статически настроенные как EAGER. В  любом  случае,  первичный  ключ  и  версия,  если  таковые  имеются,  всегда загружаются.

Загрузить EntityGraph можем тремя способами:

  1. Используя перегруженный метод find(), который принимает Map с настройками EntityGraph:

EntityGraph entityGraph = entityManager.getEntityGraph(\"post-entity-graph\");

Map<String, Object> properties = new HashMap<>();

properties.put(\"javax.persistence.fetchgraph\", entityGraph);

Post post = entityManager.find(Post.class, id, properties);

  1. Используя JPQL и передав подсказку (hint):

EntityGraph entityGraph = entityManager.getEntityGraph(\"post-entity-graph-with- comment-users\");

Post post = entityManager.createQuery(\"select p from Post p where p.id = :id\", Post.class)

  .setParameter(\"id\", id)

  .setHint(\"javax.persistence.fetchgraph\", entityGraph)

  .getSingleResult();

  1. С помощью Criteria API:

EntityGraph entityGraph = entityManager.getEntityGraph(\"post-entity-graph-with- comment-users\");

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();

CriteriaQuery<Post> criteriaQuery = criteriaBuilder.createQuery(Post.class);

Root<Post> root = criteriaQuery.from(Post.class);

criteriaQuery.where(criteriaBuilder.equal(root.<Long>get(\"id\"), id));

TypedQuery<Post> typedQuery = entityManager.createQuery(criteriaQuery);

typedQuery.setHint(\"javax.persistence.loadgraph\", entityGraph);

Post post = typedQuery.getSingleResult();

Все они дают следующий результат:

select

    post0_.id as id1_1_0_,

    post0_.subject as subject2_1_0_,

    post0_.user_id as user_id3_1_0_,

    comments1_.post_id as post_id3_0_1_,

    comments1_.id as id1_0_1_,

    comments1_.id as id1_0_2_,

    comments1_.post_id as post_id3_0_2_,

    comments1_.reply as reply2_0_2_,

    comments1_.user_id as user_id4_0_2_,

    user2_.id as id1_2_3_,

    user2_.email as email2_2_3_,

    user2_.name as name3_2_3_

from

    Post post0_

left outer join

    Comment comments1_

        on post0_.id=comments1_.post_id

left outer join

    User user2_

        on post0_.user_id=user2_.id

where

    post0_.id=?

В каждом из них стратегия графа (fetchgraph, loadgraph) указана как подсказка. В первом примере мы использовали Map, в то время как в двух последующих примерах  мы использовали метод setHint().


Предыдущий вопрос: 37. Расскажите про проблему N+1 Select и путях ее решения.

Следующий вопрос: 1. Что такое инверсия контроля (IoC) и внедрение зависимостей (DI)? Какони реализованы в Spring?

Все вопросы по теме: список

Все темы: список

Вопросы/замечания/предложения/нашли ошибку: напишите мне

Report Page