JPA的懒加载

懒加载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时,空关系也不会造成报错,似乎没有作用,调试源码暂时还没有发现其调用逻辑。有道友知道这个功能吗?可以帮我解答一下,谢谢!

原文地址:https://www.cnblogs.com/fengli9998/p/12023527.html

时间: 2024-11-09 01:57:15

JPA的懒加载的相关文章

Spring集成JPA配置懒加载两个报错解决办法

一:报错no session 因为entitymanager对象在事物提交后就关闭了 报错的 no session相当于sql的session 解决办法:解决办法 在web.xmL配置一个过滤器 使其在这个session中的manager在结束后再关闭open <!--配置openmanager--> <filter> <filter-name>openEntity</filter-name> <filter-class>org.springfr

Jpa 对多对多实现、懒加载

参考:https://www.jianshu.com/p/91d3096f38a0 实体:Device  与 Group 是多对多的关系 Group 维护端: @Data @Entity(name = "t_group") public class Group extends BaseEntity{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; @Column(name = "c

SpringBoot JPA懒加载异常 - com.fasterxml.jackson.databind.JsonMappingException: could not initialize proxy

问题与分析 某日忽然发现在用postman测试数据时报错如下: com.fasterxml.jackson.databind.JsonMappingException: could not initialize proxy [com.cbxsoftware.cbx.attachment.entity.RefAttachment#c109ec36e60c4a89a10eabc72416d984] - no Session (through reference chain: com.cbxsoftw

转 : Hibernate懒加载深入分析

懒加载可以提高性能吗? 不可以简单的说"能",因为hibernate的关系映射拖累了SQL的性能,所以想出懒加载来弥补.只是弥补而以,不会超越.所以大家不要想着使用了懒加载总体性能就提高了,其实总体性能不下降就万幸了.----------------------------------------------------------Hibernate的lazy属性可以配置在: (常用)<set><list>标签上 ,可以取值true,false,extra   

懒加载异常的解决

<filter> <!-- 平时使用懒加载时,有可能会因为hibernate的session(或着jpa的EntityManager)在事务结束时的关闭,导致页面调用数据时,出现懒加载异常 可以这样配置解决懒加载异常,让session在页面上也能使用, 但会使session打开的时间稍微长一点,并发量大时会使性能有所下降,但一般的项目没问题 --> <filter-name>OpenSessionInViewFilter</filter-name> <

SpringMVC解决视图懒加载问题

在web.xml文件中加入相关的过滤器即可 <!-- 解决视图懒加载 --> <!-- org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter --> <filter> <filter-name>oemiv</filter-name> <filter-class>org.springframework.orm.jpa.support.OpenEntityMan

spring4+springmvc+springdataJPA+hibernate4整合懒加载问题

摘要 第一个问题 解决办法 第二个问题 解决办法 摘要 本文主要是为了解决"spring4+springmvc+springdataJPA+hibernate4整合",注解了OneToMany.ManyToOne,并配置了懒加载,然后报错的问题 第一个问题 org.hibernate.LazyInitializationException: could not initialize proxy - no Session at 解决办法: 在web.xml中,配置springSevlet

【java】itoo项目实战之hibernate 懒加载优化性能

在做itoo 3.0 的时候,考评系统想要上线,就开始导入数据了,只导入学生2万条数据,但是导入的速度特别的慢,这个慢的原因是因为导入的时候进行了过多的IO操作.但是导入成功之后,查询学生的速度更加慢,因为底层用了hibernate的hql语句进行查询的,学习过hibernate的人都知道,如果hibernate不设置懒加载的话,只有是有关联的数据都会一次性全部都查询出来,我试了试,查询2万条数据,最深的级联查询是有5层,然后发出来的语句是460条,时间大概是10s.然后就考虑使用lazy进行优

关于Hibernate中立即加载和懒加载的区别

当1表与2表之间有OneToMany(类似)关系时候, 对于1表的实体类,设置FetchType=EAGER时(也就是立即加载),取1表数据,对应2表的数据都会跟着一起加载,优点不用进行二次查询. 缺点是严重影响数据查询时间,查询速度慢. FetchType=LAZY(也就是懒加载),此时查询时间大大缩短,缺点是查询表1的数据时,查询不到2表的数据. 不会主动取查询2表的数据.