Hibernate--fetch抓取策略

本文介绍了Hibernate的fetch策略,包括默认的懒加载和全加载(fetch=join)。重点讨论了如何通过xml和注解配置fetch策略以减少SQL查询次数,如设置batch-size以提高性能。同时提到了fetch=join不能与count(*)一起使用的情况以及在HQL查询时的注意事项。此外,还提出了在不同场景下优化fetch策略的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有几个需要注意的地方:

1 在xml配置中 是默认懒加载的(fetch=select),也就是在使用到懒加载对象的属性时候  才会发出SQL语句,不使用的话就发一个SQL即可,若遍历时使用关联对象的属性,那么会发出大量SQL,而这是我们不希望看见的

很多情况下 我们是要希望能使用fetch=Join 也就是实现全部加载 只发出一条SQL,即使没有显示的使用关联对象属性仍然全部加载,此SQL使用left join连接表。省的在遍历时调用关联对象属性的时候 再发出大量SQL 给数据库造成巨大压力(反正是要把数据都取出来的)

2 annatation中是默认对象fetchType=eager 也就是join 而不是select懒加载,即全部加载

3 这是加载多端对象 想要加载全部

 默认在xml中会发出3条SQL语句 一条区student 一条取classroom 一条取special
 通过设置student的xml中的<many-to-one name="classroom"  column="cid" fetch="join"/>
 同样要对classroom进行设置 join抓取special
  可以完成对抓取的设置

            session=HibernateUtil.openSession();
            Student stu=(Student)session.load(Student.class,1);
            System.err.println(stu.getName()+","+
            stu.getClassroom().getName()+","+stu.getClassroom().getSpecial().getName());
4 使用fetch=join虽然可以把关联的对象全部抓取出来  但是如果不使用关联对象 也会一并查询出来
    这样也会占用内存 数据库等资源
    此时延迟加载就失效了

<span style="white-space:pre">	</span>    session=HibernateUtil.openSession();
            Student stu=(Student)session.load(Student.class,2);
            System.err.println(stu.getName());
5 因为fetch=join仅仅对Load时候有用 对HQL没有作用 用到了再发SQL
   所以此时使用默认方式fetch=select
   从多端级联查询一端解决方案2中:

1设置对象抓取的batch size在班级中设置 比如=20 表示加载student对象时一次取20个班 默认是一次一个班 
               相当于一下子把关联的很多班级都取了出来 功能上=fetch=join
              <class name="Classroom" table="t_cla" batch-size="3">
                但是占内存多 且session关闭额 就数据消失了

  <span style="white-space:pre">	</span>List<Student> stus=session.createQuery("from Student").list();
            for (Student student : stus)
            {
                System.out.println(student.getName()+student.getClassroom());
            }
        2  重要 :在HQL中使用fetch来指定抓取 可以做到只对关联对象查一次 完全类似join在Load中的功能

        这是加载关联对象时候只会发出一条SQL  但是join fetch不能和count(*)同时使用  我们计数时候 把fetch去掉就是了

            session=HibernateUtil.openSession();
            List<Student> stus=session.createQuery("select stu from Student stu join fetch stu.classroom").list();
            for (Student student : stus)
            {
                System.out.println(student.getName()+student.getClassroom());
            }

6对于从一端加载的情况  从一端fetch的情况 默认延迟加载

 session=HibernateUtil.openSession();
            Classroom cla=(Classroom)session.load(Classroom.class,1);
            System.out.println(cla.getName());//延迟加载发出一条SQL
            for (Student student : cla.getStudents())//延迟加载 cla.getStudents()会发一条SQL 取出所有students
            {
                System.out.println(student.getName());
            }
注意:多对一时候 从多端懒加载一对象(目的是为了全部加载多端对象)
        在一端加batch-size=3 表示3个一端对象对应的所有多端对象全部加载 比如3个班的所有学生加载  减少发出的SQL
同理: 一对多时候 从一端延迟加载多端对象 在一端set中batch-size="3"表示3个一端对象对应的所有多端对象全部延迟加载

 8 对于通过HQL取班级列表并且获取相应学生列表时候  fetch=join 就无效 还是select懒加载模式
      (一端取被多端关联的对象 注意和第五条区别) 2种方案解决这个问题 因为我们的目的是要尽可能一次性全部加载(通过join连接) 不要懒加载(每用一次发个SQL)

          1 在一的一端set中加入batch-size 见test08

      <set name="students" inverse="true" lazy="extra" fetch="subselect" batch-size="2">
			<key column="cid" />
			<one-to-many class="Student"/>
      </set>
           2 第二种方案可以设置fetch=sub-select 通过子查询 自动查出的班级再通过子查询查所有对应的学生
             这是个非常好的解决方法 1条SQL搞定 很好

9 batsize都是在一端设置 多级联查询一时候 在xml的class上添加  ,一级联查询多时候  在set 上添加

下面是配置文件和 测试代码

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.itany.model">

	<class name="Student" table="t_stu">
		<!--  <cache usage="read-only" /> -->
		<id name="id">
			<generator class="native"></generator>
		</id>
		<!-- 注意:version 一定要加在ID后面 property前面 -->
		<version name="version" />
		<property name="name" />
		<property name="sex" />
		<many-to-one name="classroom" column="cid" fetch="join" />
	</class>

</hibernate-mapping>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.itany.model">

<!-- 注意:多对一时候 从多端懒加载一对象(目的是为了全部加载多端对象)
           在一端加batch-size=3 表示3个一端对象对应的所有多端对象全部加载 比如3个班的所有学生加载  减少发出的SQL
	 同理: 一对多时候 从一端延迟加载多端对象 在一端set中batch-size="3"表示3个一端对象对应的所有多端对象全部延迟加载-->
	<class name="Classroom" table="t_cla" batch-size="3">
		<id name="id" >
		 <generator class="native"></generator>
		</id>
		<property name="name" />
		<property name="grade" />
		<!-- 表示一次加载2个classroom的所有学生 -->
		<set name="students" inverse="true" lazy="extra" fetch="subselect" batch-size="2">
			<key column="cid" />
			<one-to-many class="Student"/>
		</set>
		<many-to-one name="special"  column="spec_id" fetch="join"/>
	 
	</class>

</hibernate-mapping>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.itany.model">

	<class name="Special" table="t_spec">
		<id name="id" >
		 <generator class="native"></generator>
		</id>
		<property name="name" />
		<property name="type" />
		<set name="clas" inverse="true" lazy="extra">
			<key column="spec_id" />
			<one-to-many class="Classroom" />
		</set>
	 
	</class>

</hibernate-mapping>
public class TestFetch
{
    @Test
    public void test01()
    {
        Session session=null;
        Transaction trans=null;
        try 
        {
            /*
             * 默认在xml中会发出3条SQL语句 一条区student 一条取classroom 一条取special
             * 通过设置student的xml中的<many-to-one name="classroom"  column="cid" fetch="join"/>
             * 同样要对classroom进行设置 join抓取special
             * 可以完成对抓取的设置
             */
            session=HibernateUtil.openSession();
            Student stu=(Student)session.load(Student.class,1);
            System.err.println(stu.getName()+","+
            stu.getClassroom().getName()+","+stu.getClassroom().getSpecial().getName());
            
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if(null!=session)
                HibernateUtil.closeSession(session);
        }
    }
    @Test
    public void test02()
    {
        Session session=null;
        Transaction trans=null;
        try 
        {
            /*
             * 使用fetch=join虽然可以把关联的对象全部抓取出来  但是如果不使用关联对象 也会一并查询出来
             * 这样也会占用内存 数据库等资源
             * 此时延迟加载就失效了 
             */
            session=HibernateUtil.openSession();
            Student stu=(Student)session.load(Student.class,2);
            System.err.println(stu.getName());
            
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if(null!=session)
                HibernateUtil.closeSession(session);
        }
    }
    @Test
    public void test03() 
    {
        Session session=null;
        Transaction trans=null;
        try 
        {
            /*
             * 此时虽然在xml中设置了fetch=join 结果仍然发出了取classroom得SQL语句 没有合并成一句执行(即立即全部查出关联对象)
             * 因为fetch=join仅仅对Load时候有用 对HQL没有作用 用到了再发
             * 所以此时使用默认方式fetch=select 所以此时发出查询班级的SQL 
             * 
             * 解决方案:2种
             * 1 设置对象抓取的batch size在班级中设置 比如=20 表示加载student对象时一次取20个班 默认是一次一个班 
             *   相当于一下子把关联的很多班级都取了出来 功能上=fetch=join
             *   <class name="Classroom" table="t_cla" batch-size="3">
             *   但是占内存多 且session关闭额 就数据消失了
             * 2  重要 :在HQL中使用fetch来指定抓取 可以做到只对关联对象查一次 完全类似join在Load中的功能
             * 例子见test04
             */
            session=HibernateUtil.openSession();
            List<Student> stus=session.createQuery("from Student").list();
            for (Student student : stus)
            {
                System.out.println(student.getName()+student.getClassroom());
            }
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if(null!=session)
                HibernateUtil.closeSession(session);
        }
    }
    @Test
    public void test04() 
    {
        Session session=null;
        Transaction trans=null;
        try 
        {
            /*
             * 此时在普通的from Student stu join fetch stu.classroom
             * 这是加载关联对象时候只会发出一条SQL
             * 但是join fetch不能和count(*)同时使用
             * 我们计数时候 把fetch去掉就是了
             */
            session=HibernateUtil.openSession();
            List<Student> stus=session.createQuery("select stu from Student stu join fetch stu.classroom").list();
            for (Student student : stus)
            {
                System.out.println(student.getName()+student.getClassroom());
            }
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if(null!=session)
                HibernateUtil.closeSession(session);
        }
    }
    //从一端fetch的情况 默认延迟加载
    @Test
    public void test05() 
    {
        Session session=null;
        Transaction trans=null;
        try 
        {
            /*
             */
            session=HibernateUtil.openSession();
            Classroom cla=(Classroom)session.load(Classroom.class,1);
            System.out.println(cla.getName());//延迟加载发出一条SQL
            for (Student student : cla.getStudents())//延迟加载 cla.getStudents()会发一条SQL 取出所有students
            {
                System.out.println(student.getName());
            }
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if(null!=session)
                HibernateUtil.closeSession(session);
        }
    }
  //双向:以下都是。从一端fetch的情况 fetch=join
    @Test
    public void test06() 
    {
        Session session=null;
        Transaction trans=null;
        try 
        {
            /*
             */
            session=HibernateUtil.openSession();
            Classroom cla=(Classroom)session.load(Classroom.class,1);
            System.out.println(cla.getName());//一共就一条SQL 此时全部加载关联对象 虽然没有显示调用 
            for (Student student : cla.getStudents())//
            {
                System.out.println(student.getName());
            }
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if(null!=session)
                HibernateUtil.closeSession(session);
        }
    }
    @Test
    public void test07() 
    {
        Session session=null;
        Transaction trans=null;
        try 
        {
            /*对于通过HQL取班级列表并且获取相应学生列表时候  fetch=join 就无效 还是select懒加载模式
             * 2种方案解决这个问题 因为我们的目的是要尽可能一次性全部加载(通过join连接) 不要懒加载(每用一次发个SQL)
             *  1 在一的一端set中加入batch-size 见test08
             *  2 第二种方案可以设置fetch=sub-select 通过子查询 自动查出的班级再通过子查询查所有对应的学生
             *  这是个非常好的解决方法 1条SQL搞定 很好
             */
            session=HibernateUtil.openSession();
            List<Classroom> clas=session.createQuery("from Classroom").list();//执行完这步 会发一句SQL 查询所有classroom
            for (Classroom classroom : clas)
            {
                System.out.println(classroom.getName());
                for (Student student : classroom.getStudents())//每一个classroom懒加载该id的所有学生 一共发出10次 因为有10个班级
                {
                    System.out.println(student.getName());
                }
            }
            
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if(null!=session)
                HibernateUtil.closeSession(session);
        }
    }
    @Test
    public void test08() 
    {
        Session session=null;
        Transaction trans=null;
        try 
        {
            /*在classroom 中set中设置batch-size=2 在一的一端进行设置
             */
            session=HibernateUtil.openSession();
            List<Classroom> clas=session.createQuery("from Classroom").list();//
            for (Classroom classroom : clas)
            {
                System.out.println(classroom.getName());
                for (Student student : classroom.getStudents())//
                                                               //
                {
                    System.out.println(student.getName());//
                }
            }
            
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if(null!=session)
                HibernateUtil.closeSession(session);
        }
    }
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值