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

  • 相关阅读:
    'Undefined symbols for architecture i386,clang: error: linker command failed with exit code 1
    The codesign tool requires there only be one 解决办法
    XCode iOS project only shows “My Mac 64bit” but not simulator or device
    Provisioning profile XXXX can't be found 的解决办法
    UIView 中的控件事件穿透 Passthrough 的实现
    Xcode4.5出现时的OC新语法
    xcode 快捷键(持续更新)
    打越狱包
    php缓存与加速分析与汇总
    浏览器的判断
  • 原文地址:https://www.cnblogs.com/fengli9998/p/12023527.html
Copyright © 2020-2023  润新知