JPA的懒加载
JPA数据懒加载LAZY和实时加载EAGER(二)
懒加载LAZY和实时加载EAGER的概念,在各种开发语言中都有广泛应用。其目的是实现关联数据的选择性加载,懒加载是在属性被引用时,才生成查询语句,抽取相关联数据。而实时加载则是执行完主查询后,不管是否被引用,立马执行后续的关联数据查询。社区里有人认为懒加载这种功能比较鸡肋,这种事仁者见仁,智者见智啦,个人觉得依自己业务场景而定。
顺带说一句,使用懒加载来调用关联数据,必须要保证主查询session(数据库连接会话)的生命周期没有结束,否则,你是无法抽取到数据的。在Spring Data JPA中如何自己控制session的生命周期,这个功能我还没有研究,作为下一次的研究课题。
在今天解析懒加载与实时加载的实例时,需要借助实体关系映射功能,配置实体间的一对多(OneToMany)和多对一(ManyToOne)关系。当然,还有OneToOne、ManyToMany关系,这个如有兴趣大家就自己研究下了。
一、为实体类ProcessBlock和Node添加一对多的关联关系。在我这个示例中ProcessBlock是流程定义类,一个流程定义对应多个流程节点,即Node表。下面给出代码。
1、ProcessBlock实体定义

@Entity(name = "nbpm_processblock")
@NamedQuery(name = "ProcessBlock.findByName", query = "select p from nbpm_processblock p where p.name=?1")
public class ProcessBlock implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "id")
long id;
@Column(name = "name")
String name;
@Column(name = "description")
String description;
@OneToMany(mappedBy="processblock",cascade=CascadeType.ALL,fetch = FetchType.EAGER)
Set<Node> nodeSet = new HashSet<Node>();
public long getId() {
return id;
}
……

定义Set<Node>属性 nodeSet,为其添加注解@OneToMany,意思就是一个ProcessBlock实体对应多个Node节点。注解属性 mappedBy,是指关联关系可以从Node类中的processblock属性上获取;cascade属性设置成CascadeType.ALL,是说主从表全面建立级联关系;fetch属性设置成FetchType.EAGER,是指加载规则是即时加载。查看源码会发现默认情况下,OneToMany注解的fetch属性设置的是FetchType.LAZY。
2、Node实体定义

@Entity(name = "nbpm_node")
public class Node implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "id")
long id;
@Column(name = "name")
String name;
@Column(name = "description")
String description;
@Column(name = "subclass")
String subclass;
/**
* 关联关系必须存在
*/
@ManyToOne(optional = true)
@JoinColumn(name="processblock")
ProcessBlock processblock;
public long getId() {
return id;
}
……

定义ProcessBlock属性 processblock,为其添加注解@ManyToOne,意思就是一个Node实体对应一个ProcessBlock流程定义。添加注解@JoinColumn,属性name设置成"processblock",意思是nbpm_node表中的processblock字段是外键,与ProcessBlock的主键建议关联关系。需要说明一下 ManyToOne注解的fetch属性 默认是FetchType.EAGER,查询时会立马抽取对应的主表数据。这块的考虑还是很细致的,大致就是关联数据是集合时就懒加载,是单条数据时就实时加载。
二、编写查询示例并展示结果。查询使用自定义的JpaUtil工具,工具实现请参考《自定义JpaUtil,快速完成Hql执行逻辑(一)》
1、查询ProcessBlock,主干流程数据,因为设置的nodeSet属性是实时加载加载,查询完成,断点查看确实拿到了流程对应的所有节点信息。

Map<String, Object> params = new HashMap<>();
params.put("name", "主干流程");
List<ProcessBlock> list = ApplicationContextUtil.instance.getJpaUtil().list(
"select u from simm.spring.entity.ProcessBlock u where u.name=:name", params, ProcessBlock.class); Set<Node> nodes = list.get(0).getNodeSet();



-- Hibernate框架生成的查询语句 -----------------
Hibernate: select processblo0_.id as id1_2_, processblo0_.description as descript2_2_, processblo0_.name as name3_2_ from nbpm_processblock processblo0_ where processblo0_.name=?
Hibernate: select nodeset0_.processblock as processb5_1_0_, nodeset0_.id as id1_1_0_, nodeset0_.id as id1_1_1_, nodeset0_.description as descript2_1_1_, nodeset0_.name as name3_1_1_,
nodeset0_.processblock as processb5_1_1_, nodeset0_.subclass as subclass4_1_1_
from nbpm_node nodeset0_ where nodeset0_.processblock=?
Hibernate: select nodeset0_.processblock as processb5_1_0_, nodeset0_.id as id1_1_0_, nodeset0_.id as id1_1_1_, nodeset0_.description as descript2_1_1_, nodeset0_.name as name3_1_1_,
nodeset0_.processblock as processb5_1_1_, nodeset0_.subclass as subclass4_1_1_
from nbpm_node nodeset0_ where nodeset0_.processblock=?
Hibernate: select nodeset0_.processblock as processb5_1_0_, nodeset0_.id as id1_1_0_, nodeset0_.id as id1_1_1_, nodeset0_.description as descript2_1_1_, nodeset0_.name as name3_1_1_,
nodeset0_.processblock as processb5_1_1_, nodeset0_.subclass as subclass4_1_1_
from nbpm_node nodeset0_ where nodeset0_.processblock=?
Hibernate: select nodeset0_.processblock as processb5_1_0_, nodeset0_.id as id1_1_0_, nodeset0_.id as id1_1_1_, nodeset0_.description as descript2_1_1_, nodeset0_.name as name3_1_1_,
nodeset0_.processblock as processb5_1_1_, nodeset0_.subclass as subclass4_1_1_
from nbpm_node nodeset0_ where nodeset0_.processblock=?

2、将ProcessBlock类的属性nodeSet设置成懒加载LAZY,查询后,发现对应的节点列表信息获取不到了。这里懒加载的数据读取不到是因为主查询的session已经结束。
@OneToMany(mappedBy="processblock",cascade=CascadeType.ALL,fetch = FetchType.LAZY)
//@JoinColumn(name="processblock_id")
Set<Node> nodeSet = new HashSet<Node>();

-- Hibernate框架生成的查询语句 -----------------
Hibernate: select processblo0_.id as id1_2_, processblo0_.description as descript2_2_, processblo0_.name as name3_2_ from nbpm_processblock processblo0_ where processblo0_.name=?
3、Hql直接查询关联属性
List<Node> nodeList = ApplicationContextUtil.instance.getJpaUtil().list(
"select u.nodeSet from simm.spring.entity.ProcessBlock u where u.id=1", null, Node.class);

-- Hibernate框架生成的查询语句 -----------------
Hibernate: select nodeset1_.id as id1_1_, nodeset1_.description as descript2_1_, nodeset1_.name as name3_1_, nodeset1_.processblock as processb5_1_, nodeset1_.subclass as subclass4_1_
nbpm_processblock processblo0_ inner join nbpm_node nodeset1_ on processblo0_.id=nodeset1_.processblock where processblo0_.id=1
Hibernate: select processblo0_.id as id1_2_0_, processblo0_.description as descript2_2_0_, processblo0_.name as name3_2_0_
from nbpm_processblock processblo0_ where processblo0_.id=?

至此,本次测试的主要内容就说完了,调试细节比较繁杂这里不再细表。在最后有个疑问与大家交流下,在ManyToOne这个注解中有一个optional的属性,源码里标注该属性默认设置成true,即与主表间的关联可以为null,设置成false时,则关联关系不能为空。我猜想着设置此关系后,生成的sql语句可能会有所不同,或者会引起系统运行时报错什么的。但是测试发现,并没有影响,sql语句都是left outer join关联,设置成false时,空关系也不会造成报错,似乎没有作用,调试源码暂时还没有发现其调用逻辑。有道友知道这个功能吗?可以帮我解答一下,谢谢!
JPA的懒加载的更多相关文章
- JPA数据懒加载LAZY配合事务@Transactional使用(三)
上篇博文<JPA数据懒加载LAZY和实时加载EAGER(二)>讲到,如果使用懒加载来调用关联数据,必须要保证主查询session(数据库连接会话)的生命周期没有结束,否则,你是无法抽取到数 ...
- Rest风格中关于JPA使用懒加载的坑
公司最近使用的ORM框架是JPA实现产品使用的是hibernate,曾经看过一篇博客上面说的是如果团队里面没有一个精通hibernate的人,那么最好不要使用它,我现在是深刻的体会到了.但是使用什么框 ...
- JPA数据懒加载LAZY和实时加载EAGER(二)
懒加载LAZY和实时加载EAGER的概念,在各种开发语言中都有广泛应用.其目的是实现关联数据的选择性加载,懒加载是在属性被引用时,才生成查询语句,抽取相关联数据.而实时加载则是执行完主查询后,不管是否 ...
- JPA数据懒加载LAZY和实时加载EAGER(转)
原文:https://www.cnblogs.com/MrSi/p/8081811.html 懒加载LAZY和实时加载EAGER的概念,在各种开发语言中都有广泛应用.其目的是实现关联数据的选择性加载, ...
- hibernate JPA 使用懒加载时代理对象
hibernate延迟加载代理对象实际对象读取方式 public static <T> T deproxy (T obj) { if (obj == null) return obj; i ...
- 关于Web项目出现懒加载异常的解决方案
manytomany关系中,使用 fetch = FetchType.LAZY 来做懒加载,加快些性能.但是却一直出错,原因是session被关闭,要保持session,需要事务. Hibernate ...
- jpa懒加载异常
1.项目背景概述 事情是这样子的,使用了spring data jpa的项目jeesite jeesite的实体中使用了懒加载模式. 并且一个实体类中还不止一个属性设置了懒加载模式. 项目本身已经存在 ...
- Spring Boot JPA Entity Jackson序列化触发懒加载的解决方案
Spring Jpa这项技术在Spring 开发中经常用到. 今天在做项目用到了Entity的关联懒加载,但是在返回Json的时候,不管关联数据有没有被加载,都会触发数据序列化,而如果关联关系没有被加 ...
- 解决JPA懒加载典型的N+1问题-注解@NamedEntityGraph
因为在设计一个树形结构的实体中用到了多对一,一对多的映射关系,在加载其关联对象的时候,为了性能考虑,很自然的想到了懒加载. 也由此遇到了N+1的典型问题 : 通常1的这方,通过1条SQL查找得到1个对 ...
随机推荐
- 浅谈HTML5的新特性
2014年10月29日,W3C宣布,经过接近8年的艰苦努力,HTML5标准规范终于制定完成. HTML5将会取代1999年制定的HTML 4.01.XHTML 1.0标准,使网络标准达到符合当代的网络 ...
- git的基本使用和多人协作合并管理
1.代码版本控制工具 git 分布式 svn 集中式 2.配置git 配置用户名以及邮箱账号,用于记录用户信息 git config --global user.name 'wudaxun' git ...
- Docker08-网络管理
目录 桥接网络 Bridge Network 相关操作命令 实例演示:容器之间通过自定义bridge通讯 宿主网络 Host Network Overlay Network 相关操作命令 实例演示:容 ...
- 浅谈布隆过滤器Bloom Filter
先从一道面试题开始: 给A,B两个文件,各存放50亿条URL,每条URL占用64字节,内存限制是4G,让你找出A,B文件共同的URL. 这个问题的本质在于判断一个元素是否在一个集合中.哈希表以O(1) ...
- Flask基础之返回值与form表单提交
目录 1.Python 现阶段三大主流Web框架 Django Tornado Flask 对比 2.Flask的安装 3.Flask的第一个简单应用 4.Flask中的render_template ...
- Python xlrd模块读取Excel表中的数据
1.xlrd库的安装 直接使用pip工具进行安装(当然也可以使用pycharmIDE进行安装,这里就不详述了) pip install xlrd 2.xlrd模块的一些常用命令 ①打开excel文件并 ...
- Vue.prototype 全局变量
有两种都是在main.js声明 第一种 main.js 声明 Vue.config.productionTip = false // mount axios Vue.$http and this.$h ...
- php单例型(singleton pattern)
搞定,吃饭 <?php /* The purpose of singleton pattern is to restrict instantiation of class to a single ...
- MySQL定义char和varchar类型,utf8编码,则最大值为多少?
有道面试题:若一张表中只有一个字段VARCHAR(N)类型,utf8编码,则N最大值为多少?先明白计算的一些规则限制 4.0版本以下,varchar(20),指的是20字节,如果存放UTF8汉字时,只 ...
- wp_list_categories如何去掉前面的categories
我们可以通过<?php wp_list_categories(); ?>来调用所有分类,但是在前面会出现一个categories,对强迫症患者来说就是钉子,那就把它去掉吧,顺便让自己更熟悉 ...