• 十、hibernate的延迟加载和抓取策略


    延迟加载:控制sql语句发送时机

    抓取策略:控制sql语句格式,子查询、连接查询、普通sql

    延迟加载

    延迟加载(lazy),也叫做懒加载;执行到该行代码时,不发送sql进行查询,只有在真正使用到这个对象的一些未知的属性才会真正发送sql去查询

    分类:

    • 类级别的延迟加载
    • 关联级别的延迟加载

    类级别的延迟加载

    • 使用load方法查询某个对象时是否使用延迟加载
    • 一般在映射文件的<class>标签中配置lazy属性:<class name="com.qf.entity.Parent" table="parent" lazy="false">
      • 不采用延迟:lazy="false"
      • 采用延迟:lazy="true"
    • 类级别延迟加载失效方法
      • <class>标签中配置lazy="false"
      • 使用final修饰持久化类(延迟加载内部使用javassist技术实现,需要继承持久化类,但是final类无法被继承)
      • Hibernate.initialize(obj):执行该方法会立刻发送sql进行查询

    测试

    映射文件:Parent.hbm.xml

    <hibernate-mapping>
    	<class name="com.qf.entity.Parent" table="parent" lazy="true">
    		<id name="pid" column="pid">
    			<generator class="native"/>
    		</id>
    		<property name="pname" column="pname"/>
    		<property name="age" column="age"/>
    		<set name="childs" cascade="save-update">
    			<key column="pid"/>
    			<one-to-many class="com.qf.entity.Children"  />
    		</set>
    	</class>
    </hibernate-mapping>

    测试方法

    @Test
    /**
     * HQL简单查询
     */
    public void query() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction tx = session.beginTransaction();
    	
    	Parent parent = session.load(Parent.class, 1L);//代码执行到这里并没有发送sql
    	
    	System.out.println(parent);//在真正使用的时候发送sql执行查询。此处发送sql
    	
    	tx.commit();
    }

    关联级别的延迟加载

    指的是查询到某个对象时,查询其关联的对象,需不需要延迟

    在映射文件的<set>标签或者<many-to-one>标签中配置

    <set name="childs" cascade="save-update" lazy="true">
    ----------------------------------------
    <many-to-one lazy="false" name="p" class="com.qf.entity.Parent" column="pid" />
    • set标签中的lazy的属性值可选
      • lazy="true"  :默认值,查询关联对象时,采用延迟加载
      • lazy="false"  :查询关联对象时,不采用延迟加载
      • lazy="extra"  :极其懒惰的延迟加载(用到谁查谁)
    • many-to-one标签中的lazy属性值可选
      • lazy="false"             :查询关联对象时,不采用延迟加载
      • lazy="proxy"            :默认值,proxy具体的取值,取决于另一端的<class>上的lazy值
      • lazy="no-proxy"       :不使用这个选项

    测试

    映射文件:Parent.hbm.xml

    <hibernate-mapping>
    	<class name="com.qf.entity.Parent" table="parent" lazy="true">
    		<id name="pid" column="pid">
    			<generator class="native"/>
    		</id>
    		<property name="pname" column="pname"/>
    		<property name="age" column="age"/>
    		<set name="childs" cascade="save-update" lazy="true">
    			<key column="pid"/>
    			<one-to-many class="com.qf.entity.Children"  />
    		</set>
    	</class>
    </hibernate-mapping>

    测试方法

    @Test
    /**
     * HQL简单查询
     */
    public void query() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction tx = session.beginTransaction();
    	
    	Parent parent = session.get(Parent.class, 1L);//第一次发送sql,查询parent对象
    	Set<Children> set = parent.getChilds();//不发送sql
    	
    	System.out.println(set.size());//第二次发送sql,查询children对象
    	
    	tx.commit();
    }

    抓取策略

    简介

    • 应用程序需要在(Hibernate实体对象图的)关联关系间进行导航的时候, Hibernate如何获取关联对象的策略
      • 也就是通过一个对象抓取到关联的对象,需要发送什么sql,怎么发送,发送成什么格式
    • 抓取策略通常和关联级别的延迟加载一起使用

    使用

    • 通常在<set>、<many-to-one>标签上通过fetch属性进行设置
    • 通过fetch+lazy的设置进而优化发送的sql

    在set标签上配置

    fetch:抓取策略,控制SQL语句格式

    • select :默认值,发送普通的select语句,查询关联对象
    • join :发送一条迫切左外连接查询关联对象
    • subselect :发送一条子查询查询其关联对象

    1. fetch="select" lazy="true"(默认)

    映射文件中set标签配置

    <set name="childs" cascade="save-update" lazy="true" fetch="select">
    	<key column="pid"/>
    	<one-to-many class="com.qf.entity.Children"  />
    </set>

    测试方法

    @Test
    /**
     * fetch="select" lazy="true"(默认)
     */
    public void query() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction tx = session.beginTransaction();
    	Parent parent = session.load(Parent.class, 1L);//不发送sql
    	Set<Children> childs = parent.getChilds();//发送第一条sql查询parent对象
    	for (Children children : childs) {//发送第二条sql查询children对象,懒加载,用到的时候才会查询
    
    		System.out.println(children.getCname());
    	}
    	tx.commit();
    }

    2. fetch="select" lazy="false"

    @Test
    /**
     * fetch="select" lazy="false"
     */
    public void query() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction tx = session.beginTransaction();
    	Parent parent = session.load(Parent.class, 1L);//不发送sql
    	Set<Children> childs = parent.getChilds();//发送两条sql,第一条查询parent对象,第二条sql查询children的集合对象
    	System.out.println(childs.size());
    	tx.commit();
    }

    -------------------------------console-------------------------------

    Hibernate: 
        select
            parent0_.pid as pid1_1_0_,
            parent0_.pname as pname2_1_0_,
            parent0_.age as age3_1_0_ 
        from
            parent parent0_ 
        where
            parent0_.pid=?
    Hibernate: 
        select
            childs0_.pid as pid4_0_0_,
            childs0_.cid as cid1_0_0_,
            childs0_.cid as cid1_0_1_,
            childs0_.cname as cname2_0_1_,
            childs0_.sex as sex3_0_1_,
            childs0_.pid as pid4_0_1_ 
        from
            children childs0_ 
        where
            childs0_.pid=?
    10

    3. fetch="select" lazy="extra"

    @Test
    /**
     * fetch="select" lazy="extra"
     */
    public void query() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction tx = session.beginTransaction();
    	Parent parent = session.load(Parent.class, 1L);//不发送sql
    	Set<Children> childs = parent.getChilds();//发送第一条sql查询parent对象
    	System.out.println(childs.size());//发送第一条sql查询childs总数,比lazy="true"的还要懒惰,需要用到什么结果就只查询什么
    	tx.commit();
    }

    -------------------------------console-------------------------------

    Hibernate: 
        select
            parent0_.pid as pid1_1_0_,
            parent0_.pname as pname2_1_0_,
            parent0_.age as age3_1_0_ 
        from
            parent parent0_ 
        where
            parent0_.pid=?
    Hibernate: 
        select
            count(cid) 
        from
            children 
        where
            pid =?
    10

    4. fetch="join" 

    发送一条迫切左外连接查询关联对象,此时配置lazy就没有作用了,因为两张表关联的数据都会查出来

    @Test
    /**
     * fetch="join" lazy配置会失效
     */
    public void query() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction tx = session.beginTransaction();
    	Parent parent = session.load(Parent.class, 1L);//不发送sql
    	Set<Children> childs = parent.getChilds();//发送一条sql,查询parent和children的所有字段,全部信息都查出来了
    	System.out.println(childs.size());
    	tx.commit();
    }

    -------------------------------console-------------------------------

    Hibernate: 
        select
            parent0_.pid as pid1_1_0_,
            parent0_.pname as pname2_1_0_,
            parent0_.age as age3_1_0_,
            childs1_.pid as pid4_0_1_,
            childs1_.cid as cid1_0_1_,
            childs1_.cid as cid1_0_2_,
            childs1_.cname as cname2_0_2_,
            childs1_.sex as sex3_0_2_,
            childs1_.pid as pid4_0_2_ 
        from
            parent parent0_ 
        left outer join
            children childs1_ 
                on parent0_.pid=childs1_.pid 
        where
            parent0_.pid=?
    10

    5. fetch="subselect" lazy="true"

    @Test
    /**
     * fetch="subselect" lazy="true"
     */
    public void query() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction tx = session.beginTransaction();
    	Parent parent = session.load(Parent.class, 1L);//不发送sql
    	Set<Children> childs = parent.getChilds();//发送sql查询parent对象
    	System.out.println(childs.size());//发送sql查询children对象(因为parent只有一个所以子查询效果没有显现)
    	tx.commit();
    }

    -------------------------------console-------------------------------

    Hibernate: 
        select
            parent0_.pid as pid1_1_0_,
            parent0_.pname as pname2_1_0_,
            parent0_.age as age3_1_0_ 
        from
            parent parent0_ 
        where
            parent0_.pid=?
    Hibernate: 
        select
            childs0_.pid as pid4_0_0_,
            childs0_.cid as cid1_0_0_,
            childs0_.cid as cid1_0_1_,
            childs0_.cname as cname2_0_1_,
            childs0_.sex as sex3_0_1_,
            childs0_.pid as pid4_0_1_ 
        from
            children childs0_ 
        where
            childs0_.pid=?
    10

    @Test
    /**
     * fetch="subselect" lazy="true"
     */
    public void query() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction tx = session.beginTransaction();
    	
    	Query query = session.createQuery("from Parent");
    	List<Parent> list = query.list();
    	for (Parent parent : list) {
    		System.out.println(parent.getChilds().size());
    	}
    	
    	tx.commit();
    }

    -------------------------------console-------------------------------

    Hibernate: 
        select
            parent0_.pid as pid1_1_,
            parent0_.pname as pname2_1_,
            parent0_.age as age3_1_ 
        from
            parent parent0_
    Hibernate: 
        select
            childs0_.pid as pid4_0_1_,
            childs0_.cid as cid1_0_1_,
            childs0_.cid as cid1_0_0_,
            childs0_.cname as cname2_0_0_,
            childs0_.sex as sex3_0_0_,
            childs0_.pid as pid4_0_0_ 
        from
            children childs0_ 
        where
            childs0_.pid in (
                select
                    parent0_.pid 
                from
                    parent parent0_
            )
    10
    10
    10

    fetch="subselect" lazy="false"和fetch="subselect" lazy="extra"类似

    在many-to-one标签上配置

    fetch

    • select:默认
    • join

    lazy

    • false
    • proxy:默认,proxy具体的取值,取决于另一端的<class>上的lazy的值
      • 另一端<class lazy="true">,那么这一端的lazy="proxy"就相当于lazy="true"
      • 另一端<class lazy="false">,那么这一端的lazy="proxy"就相当于lazy="false"
    • no-proxy

    1. fetch="select" lazy="proxy"(默认)

    映射文件many-to-one设置

    <many-to-one name="p" class="com.qf.entity.Parent" column="pid" lazy="proxy" fetch="select"/>

    测试方法

    @Test
    /**
     * lazy="proxy" fetch="select"
     */
    public void query() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction tx = session.beginTransaction();
    	
    	Children children = session.load(Children.class, 1L);
    	System.out.println("childernName:"+children.getCname());//发送一条sql查询children对象
    System.out.println("parentName:"+children.getP().getPname());//发送一条sql查询parent对象 tx.commit(); }

    -------------------------------console-------------------------------

    Hibernate: 
        select
            children0_.cid as cid1_0_0_,
            children0_.cname as cname2_0_0_,
            children0_.sex as sex3_0_0_,
            children0_.pid as pid4_0_0_ 
        from
            children children0_ 
        where
            children0_.cid=?
    childernName:张三8
    Hibernate: 
        select
            parent0_.pid as pid1_1_0_,
            parent0_.pname as pname2_1_0_,
            parent0_.age as age3_1_0_ 
        from
            parent parent0_ 
        where
            parent0_.pid=?
    parentName:老张

    2. fetch="select" lazy="false"

    @Test
    /**
     * lazy="false" fetch="select"
     */
    public void query() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction tx = session.beginTransaction();
    	
    	Children children = session.load(Children.class, 1L);
    	System.out.println("childernName:"+children.getCname());//发送两条sql,第一条查询children对象,第二条查询parent对象
    	
    	System.out.println("parentName:"+children.getP().getPname());
    	
    	tx.commit();
    }

    -------------------------------console-------------------------------

    Hibernate: 
        select
            children0_.cid as cid1_0_0_,
            children0_.cname as cname2_0_0_,
            children0_.sex as sex3_0_0_,
            children0_.pid as pid4_0_0_ 
        from
            children children0_ 
        where
            children0_.cid=?
    Hibernate: 
        select
            parent0_.pid as pid1_1_0_,
            parent0_.pname as pname2_1_0_,
            parent0_.age as age3_1_0_ 
        from
            parent parent0_ 
        where
            parent0_.pid=?
    childernName:张三8
    parentName:老张

    3. fetch="join" 

    发送迫切左外连接,lazy失效

    @Test
    /**
     * fetch="join" lazy失效 
     */
    public void query() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction tx = session.beginTransaction();
    	
    	Children children = session.load(Children.class, 1L);
    	System.out.println("childernName:"+children.getCname());//发送一条sql,同时查询children和parent的所有字段
    	
    	System.out.println("parentName:"+children.getP().getPname());
    	
    	tx.commit();
    }

    -------------------------------console-------------------------------

    Hibernate: 
        select
            children0_.cid as cid1_0_0_,
            children0_.cname as cname2_0_0_,
            children0_.sex as sex3_0_0_,
            children0_.pid as pid4_0_0_,
            parent1_.pid as pid1_1_1_,
            parent1_.pname as pname2_1_1_,
            parent1_.age as age3_1_1_ 
        from
            children children0_ 
        left outer join
            parent parent1_ 
                on children0_.pid=parent1_.pid 
        where
            children0_.cid=?
    childernName:张三8
    parentName:老张

     批量抓取

    一次批量获取一批关联对象,设置batch-size属性

    • batch-size="5",表示一次抓取5批(例如:查询所有parent,并使用到每个parent的children,batch-size="5"就表示一次获取5个parent的所有children对象)

    一的一方批量抓取多的一方

    parent和children,查出所有parent,再找出每个parent所有的children

    • 需要在一的一方的set标签中设置

    1. 不配置批量抓取测试

    映射文件set标签配置

    <set name="childs" cascade="save-update" >
    	<key column="pid"/>
    	<one-to-many class="com.qf.entity.Children"  />
    </set>

    测试方法

    @Test
    public void query() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction tx = session.beginTransaction();
    	
    	Query query = session.createQuery("from Parent");
    	List<Parent> list = query.list();
    	for (Parent parent : list) {
    		System.out.println("parentName : "+parent.getPname());
    		Set<Children> childs = parent.getChilds();
    		for (Children children : childs) {
    			System.out.println("childrenName : "+children.getCname());
    		}
    		
    	}
    	
    	tx.commit();
    }

    -------------------------------console-------------------------------

    Hibernate: 
        select
            parent0_.pid as pid1_1_,
            parent0_.pname as pname2_1_,
            parent0_.age as age3_1_ 
        from
            parent parent0_
    parentName : 老张
    Hibernate: 
        select
            childs0_.pid as pid4_0_0_,
            childs0_.cid as cid1_0_0_,
            childs0_.cid as cid1_0_1_,
            childs0_.cname as cname2_0_1_,
            childs0_.sex as sex3_0_1_,
            childs0_.pid as pid4_0_1_ 
        from
            children childs0_ 
        where
            childs0_.pid=?
    childrenName : 张三2
    childrenName : 张三0
    childrenName : 张三8
    parentName : 老李
    Hibernate: 
        select
            childs0_.pid as pid4_0_0_,
            childs0_.cid as cid1_0_0_,
            childs0_.cid as cid1_0_1_,
            childs0_.cname as cname2_0_1_,
            childs0_.sex as sex3_0_1_,
            childs0_.pid as pid4_0_1_ 
        from
            children childs0_ 
        where
            childs0_.pid=?
    childrenName : 李四9
    childrenName : 李四6
    childrenName : 李四3
    parentName : 老王
    Hibernate: 
        select
            childs0_.pid as pid4_0_0_,
            childs0_.cid as cid1_0_0_,
            childs0_.cid as cid1_0_1_,
            childs0_.cname as cname2_0_1_,
            childs0_.sex as sex3_0_1_,
            childs0_.pid as pid4_0_1_ 
        from
            children childs0_ 
        where
            childs0_.pid=?
    childrenName : 王五2
    childrenName : 王五7
    childrenName : 王五4

    2. 配置批量抓取测试

    映射文件set标签配置

    <set name="childs" cascade="save-update" batch-size="3">
    	<key column="pid"/>
    	<one-to-many class="com.qf.entity.Children"  />
    </set>

    测试方法

    @Test
    /**
     * batch-size="3"
     */
    public void query() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction tx = session.beginTransaction();
    	
    	Query query = session.createQuery("from Parent");
    	List<Parent> list = query.list();
    	for (Parent parent : list) {
    		System.out.println("parentName : "+parent.getPname());
    		Set<Children> childs = parent.getChilds();
    		for (Children children : childs) {
    			System.out.println("childrenName : "+children.getCname());
    		}
    		
    	}
    	
    	tx.commit();
    }

    -------------------------------console-------------------------------

    Hibernate: 
        select
            parent0_.pid as pid1_1_,
            parent0_.pname as pname2_1_,
            parent0_.age as age3_1_ 
        from
            parent parent0_
    parentName : 老张
    Hibernate: 
        select
            childs0_.pid as pid4_0_1_,
            childs0_.cid as cid1_0_1_,
            childs0_.cid as cid1_0_0_,
            childs0_.cname as cname2_0_0_,
            childs0_.sex as sex3_0_0_,
            childs0_.pid as pid4_0_0_ 
        from
            children childs0_ 
        where
            childs0_.pid in (
                ?, ?, ?
            )
    childrenName : 张三8
    childrenName : 张三0
    childrenName : 张三2
    parentName : 老李
    childrenName : 李四3
    childrenName : 李四6
    childrenName : 李四9
    parentName : 老王
    childrenName : 王五2
    childrenName : 王五7
    childrenName : 王五4

    多的一方批量抓取一的一方

     查询所有children,找出每个children的parent

    • 需要在一的一方的class标签中设置batch-size

    映射文件class标签配置

    <class name="com.qf.entity.Parent" table="parent" batch-size="3">

    测试方法

    @Test
    /**
     * batch-size="3"
     */
    public void query() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction tx = session.beginTransaction();
    	
    	Query query = session.createQuery("from Children");
    	List<Children> list = query.list();
    	for (Children children : list) {
    		System.out.println("childrenName : "+children.getCname());
    		System.out.println("parentName : "+children.getP().getPname());
    	}
    	
    	tx.commit();
    }

    -------------------------------console-------------------------------

    Hibernate: 
        select
            children0_.cid as cid1_0_,
            children0_.cname as cname2_0_,
            children0_.sex as sex3_0_,
            children0_.pid as pid4_0_ 
        from
            children children0_
    childrenName : 张三8
    Hibernate: 
        select
            parent0_.pid as pid1_1_0_,
            parent0_.pname as pname2_1_0_,
            parent0_.age as age3_1_0_ 
        from
            parent parent0_ 
        where
            parent0_.pid in (
                ?, ?, ?
            )
    parentName : 老张
    childrenName : 张三0
    parentName : 老张
    childrenName : 张三2
    parentName : 老张
    childrenName : 李四9
    parentName : 老李
    childrenName : 李四6
    parentName : 老李
    childrenName : 李四3
    parentName : 老李
    childrenName : 王五7
    parentName : 老王
    childrenName : 王五2
    parentName : 老王
    childrenName : 王五4
    parentName : 老王
    

      

  • 相关阅读:
    结合人工智能的高性能医学:现状、挑战与未来
    2019年人工智能行业25大趋势
    睡眠分期--深度学习算法
    并行技术
    元数据--你有没有注意?
    战略、策略与执行
    机器学习前沿03
    元学习--learn to learn
    机器学习前沿02
    机器学习玩法
  • 原文地址:https://www.cnblogs.com/qf123/p/10186056.html
Copyright © 2020-2023  润新知