java 懒加载map_JPA的懒加载

懒加载LAZY和实时加载EAGER的概念,在各种开发语言中都有广泛应用。其目的是实现关联数据的选择性加载,懒加载是在属性被引用时,才生成查询语句,抽取相关联数据。而实时加载则是执行完主查询后,不管是否被引用,立马执行后续的关联数据查询。社区里有人认为懒加载这种功能比较鸡肋,这种事仁者见仁,智者见智啦,个人觉得依自己业务场景而定。

顺带说一句,使用懒加载来调用关联数据,必须要保证主查询session(数据库连接会话)的生命周期没有结束,否则,你是无法抽取到数据的。在Spring Data JPA中如何自己控制session的生命周期,这个功能我还没有研究,作为下一次的研究课题。

在今天解析懒加载与实时加载的实例时,需要借助实体关系映射功能,配置实体间的一对多(OneToMany)和多对一(ManyToOne)关系。当然,还有OneToOne、ManyToMany关系,这个如有兴趣大家就自己研究下了。

一、为实体类ProcessBlock和Node添加一对多的关联关系。在我这个示例中ProcessBlock是流程定义类,一个流程定义对应多个流程节点,即Node表。下面给出代码。

1、ProcessBlock实体定义

48304ba5e6f9fe08f3fa1abda7d326ab.png

@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 nodeSet = new HashSet();public long getId() {

return id;

}

……

48304ba5e6f9fe08f3fa1abda7d326ab.png

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

2、Node实体定义

48304ba5e6f9fe08f3fa1abda7d326ab.png

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

}

……

48304ba5e6f9fe08f3fa1abda7d326ab.png

定义ProcessBlock属性 processblock,为其添加注解@ManyToOne,意思就是一个Node实体对应一个ProcessBlock流程定义。添加注解@JoinColumn,属性name设置成"processblock",意思是nbpm_node表中的processblock字段是外键,与ProcessBlock的主键建议关联关系。需要说明一下 ManyToOne注解的fetch属性 默认是FetchType.EAGER,查询时会立马抽取对应的主表数据。这块的考虑还是很细致的,大致就是关联数据是集合时就懒加载,是单条数据时就实时加载。

二、编写查询示例并展示结果。查询使用自定义的JpaUtil工具,工具实现请参考《自定义JpaUtil,快速完成Hql执行逻辑(一)》

1、查询ProcessBlock,主干流程数据,因为设置的nodeSet属性是实时加载加载,查询完成,断点查看确实拿到了流程对应的所有节点信息。

48304ba5e6f9fe08f3fa1abda7d326ab.png

Map params = new HashMap<>();

params.put("name", "主干流程");

List list = ApplicationContextUtil.instance.getJpaUtil().list(

"select u from simm.spring.entity.ProcessBlock u where u.name=:name", params, ProcessBlock.class);

Set nodes = list.get(0).getNodeSet();

48304ba5e6f9fe08f3fa1abda7d326ab.png

cc92f26b6d9f068068ddac255e3a4f07.png

48304ba5e6f9fe08f3fa1abda7d326ab.png

-- 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=?

48304ba5e6f9fe08f3fa1abda7d326ab.png

2、将ProcessBlock类的属性nodeSet设置成懒加载LAZY,查询后,发现对应的节点列表信息获取不到了。这里懒加载的数据读取不到是因为主查询的session已经结束。

@OneToMany(mappedBy="processblock",cascade=CascadeType.ALL,fetch =FetchType.LAZY)

//@JoinColumn(name="processblock_id")

Set nodeSet = new HashSet();

57ab911d7f6ab7a84807ea0d8bb20c37.png

-- 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 nodeList = ApplicationContextUtil.instance.getJpaUtil().list(

"select u.nodeSet from simm.spring.entity.ProcessBlock u where u.id=1", null, Node.class);

48304ba5e6f9fe08f3fa1abda7d326ab.png

-- 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=?

48304ba5e6f9fe08f3fa1abda7d326ab.png

至此,本次测试的主要内容就说完了,调试细节比较繁杂这里不再细表。在最后有个疑问与大家交流下,在ManyToOne这个注解中有一个optional的属性,源码里标注该属性默认设置成true,即与主表间的关联可以为null,设置成false时,则关联关系不能为空。我猜想着设置此关系后,生成的sql语句可能会有所不同,或者会引起系统运行时报错什么的。但是测试发现,并没有影响,sql语句都是left outer join关联,设置成false时,空关系也不会造成报错,似乎没有作用,调试源码暂时还没有发现其调用逻辑。有道友知道这个功能吗?可以帮我解答一下,谢谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值