JPA 2.1实体图–第1部分:命名实体图

本文介绍了JPA 2.1中的实体图特性,作为解决延迟加载问题的一个更好方案。通过实体图定义,可以指定在查询时加载的属性,从而避免额外的数据库查询。文章通过示例展示了如何定义和使用命名实体图及命名子图,以及在实际应用中如何减少查询次数,提高性能。
摘要由CSDN通过智能技术生成

延迟加载通常是JPA 2.0的问题。 如果要使用FetchType.LAZY(默认)或FetchType.EAGER来加载关系,则必须在实体上进行定义,并且始终使用此模式。 仅当我们要始终加载关系时才使用FetchType.EAGER。 FetchType.LAZY几乎在所有情况下都用于获得性能良好且可伸缩的应用程序。

但这并非没有缺点。 如果必须使用关系的元素,则需要确保在从数据库加载实体的事务中初始化该关系。 这可以通过使用特定的查询来完成,该查询从数据库中读取实体和所需的关系。 但这将导致用例特定的查询。 另一个选择是在您的业务代码中访问该关系,这将导致对每个关系的附加查询。 两种方法都不完美。

JPA 2.1实体图是一个更好的解决方案。 实体图的定义与查询无关,并且定义了要从数据库中获取哪些属性。 实体图可以用作访存图或负载图。 如果使用提取图,则仅由实体图指定的属性将被视为FetchType.EAGER。 所有其他属性将是惰性的。 如果使用负载图,则实体图未指定的所有属性将保留其默认提取类型。

让我们看看如何定义和使用实体图。

示例实体

在此示例中,我们将使用带有项目列表的订单,每个项目都有一个产品。 所有关系都是懒惰的。

订单实体:

@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实体:

@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;

产品实体:

@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;

命名实体图

命名实体图的定义是通过实体上的@NamedEntityGraph批注完成的。 它定义了唯一的名称和已加载的属性列表( attributeNodes )。

以下示例显示了实体图“ graph.Order.items”的定义,该图将加载OrderOrderItem列表。

@Entity
@Table(name = "purchase_order")
@NamedEntityGraph(name = "graph.Order.items", 
      attributeNodes = @NamedAttributeNode("items"))
public class Order implements Serializable {

   ...

现在我们已经定义了实体图,我们可以在查询中使用它。 因此,我们需要创建一个具有查询提示的Map,并将其设置为find或query方法调用上的附加参数。

以下代码段显示了如何在find语句中使用命名实体图作为获取图。

EntityGraph graph = this.em.getEntityGraph("graph.Order.items");

Map hints = new HashMap();
hints.put("javax.persistence.fetchgraph", graph);

return this.em.find(Order.class, orderId, hints);

命名子图

我们使用实体图来定义Order实体的提取操作。 如果我们想对OrderItem实体执行相同的操作,则可以使用实体子图执行此操作。 命名子图的定义类似于命名实体图的定义,并且可以引用为attributeNode。

以下代码段显示了用于加载每个OrderItemProduct的子图的定义。 定义的实体图将获取包含所有OrderItem及其产品Order

@Entity
@Table(name = "purchase_order")
@NamedEntityGraph(name = "graph.Order.items", 
               attributeNodes = @NamedAttributeNode(value = "items", subgraph = "items"), 
               subgraphs = @NamedSubgraph(name = "items", attributeNodes = @NamedAttributeNode("product")))
public class Order implements Serializable {

里面发生什么事了?

好的,从发展的角度来看,实体图很棒。 它们易于使用,我们不需要编写其他代码来避免延迟加载问题。 但是里面发生了什么? 有多少查询发送到数据库? 让我们看看休眠调试日志。

2014-03-22 21:56:08,285 DEBUG [org.hibernate.loader.plan.build.spi.LoadPlanTreePrinter] (pool-2-thread-1) LoadPlan(entity=blog.thoughts.on.java.jpa21.entity.graph.model.Order) - Returns - EntityReturnImpl(entity=blog.thoughts.on.java.jpa21.entity.graph.model.Order, querySpaceUid=<gen:0>, path=blog.thoughts.on.java.jpa21.entity.graph.model.Order) - CollectionAttributeFetchImpl(collection=blog.thoughts.on.java.jpa21.entity.graph.model.Order.items, querySpaceUid=<gen:1>, 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=<gen:2>, path=blog.thoughts.on.java.jpa21.entity.graph.model.Order.items.<elements>) - EntityAttributeFetchImpl(entity=blog.thoughts.on.java.jpa21.entity.graph.model.Product, querySpaceUid=<gen:3>, path=blog.thoughts.on.java.jpa21.entity.graph.model.Order.items.<elements>.product) - QuerySpaces - EntityQuerySpaceImpl(uid=<gen:0>, 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)) : <gen:0> -> <gen:1> - CollectionQuerySpaceImpl(uid=<gen:1>, 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)) : <gen:1> -> <gen:2> - EntityQuerySpaceImpl(uid=<gen:2>, entity=blog.thoughts.on.java.jpa21.entity.graph.model.OrderItem) - SQL table alias mapping - items1_ - alias suffix - 2_ - suffixed key columns - {id1_0_2_} - JOIN (JoinDefinedByMetadata(product)) : <gen:2> -> <gen:3> - EntityQuerySpaceImpl(uid=<gen:3>, entity=blog.thoughts.on.java.jpa21.entity.graph.model.Product) - SQL table alias mapping - product2_ - alias suffix - 3_ - suffixed key columns - {id1_1_3_} 

2014-03-22 21:56:08,285 DEBUG [org.hibernate.loader.entity.plan.EntityLoader] (pool-2-thread-1) 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_, product2_.id as id1_1_3_, product2_.name as name2_1_3_, product2_.version as version3_1_3_ from purchase_order order0_ left outer join OrderItem items1_ on order0_.id=items1_.order_id left outer join Product product2_ on items1_.product_id=product2_.id where order0_.id=?

日志显示仅创建了一个查询。 Hibernate使用实体图来创建具有所有3个实体( OrderOrderItemProduct )的加载计划,并通过一个查询加载它们。

结论

我们定义了一个实体图,该图告诉实体管理器从数据库中获取3个相关实体的图( OrderOrderItemProduct )。 实体图的定义和用法与查询无关,并且仅产生一条select语句。 因此,解决了JPA 2.0方法的主要缺点(在开头提到)。

从我的角度来看,新的实体图功能确实很棒,并且可以解决延迟加载问题。 你怎么看待这件事?

翻译自: https://www.javacodegeeks.com/2014/05/jpa-2-1-entity-graph-part-1-named-entity-graphs.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值