Статьи

Графический объект JPA 2.1. Часть 2. Определение отложенной / активной загрузки во время выполнения

Это мой второй пост на JPA 2.1 Entity Graphs. Первый пост описывает использование именованных графов сущностей. Они могут использоваться для определения графа сущностей и / или атрибутов во время компиляции, которые должны быть получены с помощью метода find или query. Динамические графы сущностей делают то же самое, но динамически. Это означает, что вы можете использовать EntityGraph API для определения графа сущностей во время выполнения.

Если вы пропустили первый пост и хотите прочитать, как определить граф именованного объекта или как проблемы отложенной загрузки были решены с помощью JPA 2.0, проверьте этот пост: JPA 2.1 Entity Graph — Часть 1: Именованные графы объектов .

Пример сущностей

Мы будем использовать тот же пример, что и в предыдущем посте . Таким образом, вы можете пропустить этот абзац, если вы прочитали другой.

Мы будем использовать 3 объекта. Это Order , OrderItem и Product . Заказ может включать в себя несколько OrderItems, и каждый OrderItem принадлежит одному продукту . FetchType всех этих отношений это FetchType.LAZY. Таким образом, менеджер сущностей не будет извлекать их из базы данных по умолчанию и вместо этого инициализирует их прокси.

Субъект заказа:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
@Entity
@Table(name = "purchaseOrder")
@NamedEntityGraph(name = "graph.Order.items",
               attributeNodes = @NamedAttributeNode(value = "items", subgraph = "items"),
               subgraphs = @NamedSubgraph(name = "items", attributeNodes = @NamedAttributeNode("product")))
public class Order implements Serializable {
 
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   @Column(name = "id", updatable = false, nullable = false)
   private Long id = null;
   @Version
   @Column(name = "version")
   private int version = 0;
 
   @Column
   private String orderNumber;
 
   @OneToMany(mappedBy = "order", fetch = FetchType.LAZY)
   private Set<OrderItem> items = new HashSet<OrderItem>();
 
   ...

Сущность OrderItem:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
@Entity
public class OrderItem implements Serializable
{
 
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   @Column(name = "id", updatable = false, nullable = false)
   private Long id = null;
   @Version
   @Column(name = "version")
   private int version = 0;
 
   @Column
   private int quantity;
 
   @ManyToOne
   private Order order;
 
   @ManyToOne(fetch = FetchType.LAZY)
   private Product product;

Сущность продукта:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
@Entity
public class Product implements Serializable
{
 
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   @Column(name = "id", updatable = false, nullable = false)
   private Long id = null;
   @Version
   @Column(name = "version")
   private int version = 0;
 
   @Column
   private String name;

Динамический объектный граф

Итак, давайте определим динамический граф сущностей. Мы сделаем то же самое, что и в первом посте, и определим простой граф сущностей, который говорит менеджеру сущностей выбрать Order со всеми связанными OrderItem . Поэтому мы используем метод createEntityGraph (Class rootType) менеджера сущностей для создания графа сущностей для сущности Order . На следующем шаге мы создадим список всех атрибутов сущности Order, которые должны быть извлечены с этим графом сущности. Нам нужно только добавить элементы атрибута, потому что мы будем использовать этот граф сущностей в качестве загрузочного графика, а все остальные атрибуты настроены по умолчанию.

Если бы мы использовали этот граф сущностей в качестве выборочного графика, нам нужно было бы добавить в список все атрибуты, которые должны быть получены из базы данных.

1
2
3
4
5
6
7
EntityGraph<Order> graph = this.em.createEntityGraph(Order.class);
graph.addAttributeNodes("items");
 
Map<String, Object> hints = new HashMap<String, Object>();
hints.put("javax.persistence.loadgraph", graph);
 
this.em.find(Order.class, orderId, hints);

Хорошо, динамически определять, какие атрибуты объекта должны быть извлечены из базы данных — это хорошо. Но что, если нам нужен граф сущностей? Как получение Заказа со всеми его Элементами Заказа и их Продуктом ?

Это можно сделать с помощью подграфа. Подграф является в основном графом сущностей, который встроен в другой граф сущностей или подграф сущностей. Определение подграфа аналогично определению графа сущностей. Чтобы создать и внедрить подграф в граф сущностей, нам нужно вызвать метод addSubgraph (String attributeName) для объекта EntityGraph . Это создаст подграф для атрибута с заданным именем. На следующем шаге нам нужно определить список атрибутов, которые должны быть выбраны с этим подграфом.

В следующем фрагменте показано определение графа сущностей с подграфом сущности, который сообщает менеджеру сущностей о необходимости выбора Ордера с его OrderItem и его Продуктом .

1
2
3
4
5
6
7
8
EntityGraph<Order> graph = this.em.createEntityGraph(Order.class);
Subgraph<OrderItem> itemGraph = graph.addSubgraph("items");
itemGraph.addAttributeNodes("product");
 
Map<String, Object> hints = new HashMap<String, Object>();
hints.put("javax.persistence.loadgraph", graph);
 
return this.em.find(Order.class, orderId, hints);

Что происходит внутри?

Как и в предыдущем посте, мы хотим взглянуть на журнал гибернации и выяснить, что делает hibernate. Как мы видим, результат графа динамической сущности такой же, как и у графа именованной сущности. Создается план загрузки и один оператор выбора со всеми 3 объектами.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2014-04-07 20:08:15,260 DEBUG [org.hibernate.loader.plan.build.spi.LoadPlanTreePrinter] (default task-2) LoadPlan(entity=blog.thoughts.on.java.jpa21.entity.graph.model.Order)
    - Returns
       - EntityReturnImpl(entity=blog.thoughts.on.java.jpa21.entity.graph.model.Order, querySpaceUid=, path=blog.thoughts.on.java.jpa21.entity.graph.model.Order)
          - CollectionAttributeFetchImpl(collection=blog.thoughts.on.java.jpa21.entity.graph.model.Order.items, querySpaceUid=, path=blog.thoughts.on.java.jpa21.entity.graph.model.Order.items)
             - (collection element) CollectionFetchableElementEntityGraph(entity=blog.thoughts.on.java.jpa21.entity.graph.model.OrderItem, querySpaceUid=, path=blog.thoughts.on.java.jpa21.entity.graph.model.Order.items.)
    - QuerySpaces
       - EntityQuerySpaceImpl(uid=, entity=blog.thoughts.on.java.jpa21.entity.graph.model.Order)
          - SQL table alias mapping - order0_
          - alias suffix - 0_
          - suffixed key columns - {id1_2_0_}
          - JOIN (JoinDefinedByMetadata(items)) :  ->
             - CollectionQuerySpaceImpl(uid=, collection=blog.thoughts.on.java.jpa21.entity.graph.model.Order.items)
                - SQL table alias mapping - items1_
                - alias suffix - 1_
                - suffixed key columns - {order_id4_2_1_}
                - entity-element alias suffix - 2_
                - 2_entity-element suffixed key columns - id1_0_2_
                - JOIN (JoinDefinedByMetadata(elements)) :  ->
                   - EntityQuerySpaceImpl(uid=, entity=blog.thoughts.on.java.jpa21.entity.graph.model.OrderItem)
                      - SQL table alias mapping - items1_
                      - alias suffix - 2_
                      - suffixed key columns - {id1_0_2_}
 
2014-04-07 20:08:15,260 DEBUG [org.hibernate.loader.entity.plan.EntityLoader] (default task-2) Static select for entity blog.thoughts.on.java.jpa21.entity.graph.model.Order [NONE:-1]: select order0_.id as id1_2_0_, order0_.orderNumber as orderNum2_2_0_, order0_.version as version3_2_0_, items1_.order_id as order_id4_2_1_, items1_.id as id1_0_1_, items1_.id as id1_0_2_, items1_.order_id as order_id4_0_2_, items1_.product_id as product_5_0_2_, items1_.quantity as quantity2_0_2_, items1_.version as version3_0_2_ from purchaseOrder order0_ left outer join OrderItem items1_ on order0_.id=items1_.order_id where order0_.id=?

Вывод

После определения именованного графа сущностей в первом посте мы теперь использовали API EntityGraph для определения графа динамических сущностей. Используя этот граф сущностей, мы можем получить граф нескольких сущностей только с одним запросом из базы данных. Это может быть использовано для решения LazyInitializationException и для повышения производительности приложений.

Что вы думаете о (динамических) графах сущностей? С моей точки зрения, это очень полезное расширение по сравнению с JPA 2.0. Особенно динамические графы сущностей полезны для определения вашей стратегии выборки на основе информации времени выполнения, такой как параметры метода.