实体之间关系的延迟加载是JPA中公认的最佳实践。 它的主要目标是仅从数据库中检索请求的实体,并仅在需要时加载相关实体。 如果我们只需要请求的实体,那是一个很好的方法。 但是,如果我们还需要一些相关实体,它会增加工作量,并可能导致性能问题。

让我们看一下触发初始化的不同方法及其特定的优点和缺点。

1.在映射关系上调用方法

让我们从最显而易见的方法开始,不幸的是也从效率最低的方法开始。 我们在EntityManager上使用find方法,并在关系上调用一个方法。

Order order = this.em.find(Order.class, orderId);
order.getItems().size();

此代码工作得很好,易于阅读并且经常使用。 那么,这是什么问题呢?

好吧,您可能知道。 此代码执行附加查询以初始化关系。 这听起来不像是一个真正的问题,但是可以让我们计算出一个更加真实的场景中执行的查询的数量。

假设我们有一个具有5个关系的实体,需要初始化。 因此,我们将获得1 + 5 = 6个查询 。 好的,那是5个附加查询。 这似乎仍然不是一个大问题。

但是我们的应用程序将被多个用户并行使用(我希望)。 假设我们的系统必须为100个并行用户提供服务器。 然后,我们将获得100 + 5 * 100 = 600个查询

好的,很明显,这种方法提供了可行的解决方案,但不是一个好的解决方案。 或早或晚,额外执行的查询数量将使我们的应用程序变慢。 因此,我们应该尝试避免这种方法,并看看其他一些选择。

2.在JPQL中获取Join

初始化惰性关系的一个更好的选择是将JPQL查询与获取联接一起使用。

Query q = this.em.createQuery("SELECT o FROM Order o JOIN FETCH o.items i WHERE o.id = :id");
q.setParameter("id", orderId);
newOrder = (Order) q.getSingleResult();

这告诉实体管理器在同一查询中获取选定的实体和关系。 这种方法的优缺点很明显:

优点是所有内容都在一个查询中获取。 从性能的角度来看,这比第一种方法要好得多。

主要缺点是我们需要编写其他代码来执行查询。 但是,如果实体具有多个关系,并且我们需要针对不同的用例初始化不同的关系,那就更糟了。 在这种情况下,我们需要为获取联接关系的每个所需组合编写查询。 这会变得很混乱。

在JPQL语句中使用提取联接可能需要大量查询,这将使维护代码库变得困难。 因此,在开始编写大量查询之前,我们应该考虑可能需要的不同访存联接组合的数量。 如果数量很少,那么这是一种限制执行的查询数量的好方法。

3.获取条件API中的加入

好的,这种方法与以前的方法基本相同。 但是这次我们使用的是Criteria API,而不是JPQL查询。

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery q = cb.createQuery(Order.class);
Root o = q.from(Order.class);
o.fetch("items", JoinType.INNER);
q.select(o);
q.where(cb.equal(o.get("id"), orderId));Order order = (Order)this.em.createQuery(q).getSingleResult();

优点和缺点与带有访存连接的JPQL查询相同。 使用一个查询从数据库中检索实体和关系,我们需要每种关系组合的特定代码。 但是,如果我们使用的是Criteria API,我们通常已经有很多用例特定的查询代码。 因此,这可能不是一个大问题。

如果我们已经在使用Criteria API来构建查询,那么这是减少执行查询数量的好方法。

4.命名实体图

命名实体图是JPA 2.1的新功能。 它可用于定义应从数据库中查询的实体图。 实体图的定义是通过注释完成的,并且与查询无关。

如果您不熟悉此功能,则可以查看我以前的一篇博客文章 ,其中对它进行了更详细的介绍。

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

然后,EntityManager的find方法可以使用命名的实体图。

EntityGraph graph = this.em.getEntityGraph("graph.Order.items");Map hints = new HashMap();
hints.put("javax.persistence.fetchgraph", graph);Order order = this.em.find(Order.class, orderId, hints);

这基本上是我们第一种方法的改进版本。 实体管理器将通过一个查询从数据库检索定义的实体图。 唯一的缺点是,我们需要为将在一个查询中检索到的每种关系组合注释一个命名实体图。 与第二种方法一样,我们将需要更少的附加注释,但是它仍然会变得非常混乱。

因此,如果我们只需要定义有限数量的实体图并将其重用于不同的用例,则命名实体图是一个很好的解决方案。 否则,代码将变得难以维护。

5.动态实体图

动态实体图类似于命名实体图,并且在以前的一篇文章中也进行了解释。 唯一的区别是,实体图是通过Java API定义的。

EntityGraph graph = this.em.createEntityGraph(Order.class);
Subgraph itemGraph = graph.addSubgraph("items");Map hints = new HashMap();
hints.put("javax.persistence.loadgraph", graph);Order order = this.em.find(Order.class, orderId, hints);

通过API进行定义既可以是优点,也可以是缺点。 如果我们需要大量用例特定的实体图,则最好在特定的Java代码中定义实体图,并且不向该实体添加附加注释。 这样可以避免带有数十个注释的实体。 另一方面,动态实体图需要​​更多代码和其他方法才能重用。

因此,我建议使用动态实体图,如果我们需要定义用例特定的图,则将不会重复使用该图。 如果我们想重用实体图,则更容易注释命名的实体图。

结论

我们研究了5种不同的初始化惰性关系的方法。 正如我们所看到的,它们每个都有其优点和缺点。 那么从这篇文章中要记住什么呢?

  • 通过在映射关系上调用方法来初始化惰性关系会导致附加查询。 出于性能原因,应避免这种情况。
  • JPQL语句中的访存联接将查询数量减少到一个,但是我们可能需要很多不同的查询。
  • Criteria API还支持提取连接,对于每种需要初始化的关系,我们都需要特定的代码。
  • 如果我们将在代码中重用已定义的图,则命名实体图是一个很好的解决方案。
  • 如果我们需要定义特定于用例的图,则动态实体图可能是更好的解决方案。

翻译自: https://www.javacodegeeks.com/2014/12/5-ways-to-initialize-lazy-relations-and-when-to-use-them.html

初始化懒惰关系以及何时使用它们的5种方法相关推荐

  1. Bean的生命周期行为控制,初始化与销毁bean时执行操作的三种方法

    Bean的生命周期行为控制,初始化与销毁bean时执行操作的三种方法 一.实现Spring的接口 二.XML配置中使用 init-method和destory-method 三.使用@PostCons ...

  2. Java 初始化 List 的几种方法

    最常见的初始化 List 方法为: List<String> languages = new ArrayList<>(); languages.add("Java&q ...

  3. 简单介绍C语言使用四种方法初始化结构体

    这篇文章说明了什么是结构体,介绍了结构体的概念和使用优点,在C语言中如何使用和初始化结构体方法,通过详细的代码展开进行说明,希望该篇文章对你有所帮助 什么是结构体 在实际问题中,一组数据往往有很多种不 ...

  4. linux c 结构体初始化的四种方法

    定义: struct InitMember {int first:double second:char* third:float four; }; 方法一:定义时赋值 struct InitMembe ...

  5. java map初始化方式_java中Map和List初始化的两种方法

    第一种方法(常用方法): //初始化List List list = new ArrayList(); list.add("string1"); list.add("st ...

  6. c++vector初始化的几种方法

    1.练习1 #include <iostream> #include <string> #include <vector> #include <deque&g ...

  7. 在 Java 中初始化 List 的五种方法

    转载自  在 Java 中初始化 List 的五种方法 Java 中经常需要使用到 List,下面简单介绍几种常见的初始化方式. 1.构造 List 后使用 List.add 初始化 List< ...

  8. (转,记录用)jQuery页面加载初始化的3种方法

    jQuery 页面加载初始化的方法有3种 ,页面在加载的时候都会执行脚本,应该没什么区别,主要看习惯吧,本人觉得第二种方法最好,比较简洁. 第一种: [javascript] view plainco ...

  9. java 集合初始化_6种方法初始化JAVA中的list集合

    List 是 Java 开发中经常会使用的集合,你们知道有哪些方式可以初始化一个 List 吗?这其中不缺乏一些坑,今天栈长我给大家一一普及一下. 1.常规方式 List languages = ne ...

最新文章

  1. CGI,FastCGI与PHP
  2. linux用head显示15字符,每天一个linux命令-head
  3. pytorch模型加载测试_使用Pytorch实现物体检测(Faster R-CNN)
  4. CodeForces - 641ELittle Artem and Time Machine——map+树状数组
  5. 4问教你搞定java中的ThreadLocal
  6. 马斯克:2024年送人上火星 2050年建城
  7. 美国海关大量人脸数据泄露,暗网可以免费下载,锅被甩给外包公司
  8. foobar的来源与历史
  9. fastReport 绑定DataBand数据源后还是打印出一条数据
  10. mysql engine类型 小项目_项目中常用的19条MySQL优化
  11. 【SSL】2021-08-18 1286.恶作剧
  12. 街机中国 FBA4droid 应用
  13. ORACLE 错误 904
  14. 山地车的结构及骑行注意事项
  15. 4.1关系运算符和逻辑运算符
  16. Type string trivially inferred from a string literal, remove type annotation
  17. 微信设置“种草昵称”,个性有意思,来看看!
  18. vlookup函数和vlookup函数与数据有效性
  19. Kali google 翻译
  20. ICM-42605 6轴MEMS加速度计陀螺仪运动传感器数据的读取

热门文章

  1. double类型进行比较排序
  2. mybatisPlus的分页查询
  3. 2014蓝桥杯-B-省赛-五-圆周率
  4. linux netfilter 过滤数据包,Netfilter-iptabes报文过滤框架(一)
  5. 三条中线分的六个三角形_八年级数学上册:三角形已知两条边如何求第三边
  6. 如何将idea自带的maven添加到环境变量
  7. Redis安装与配置( Windows10 )
  8. 计算机主机组成实验,计算机组成原理实验-运算器组成实验报告
  9. 查看oracle会话和进程_带有Oracle Digital Assistant和Fn Project的会话式UI。 第三部分,迁移到云...
  10. 如何通过Rultor将Maven工件部署到CloudRepo