Hibernate--fetch抓取策略

有幾個需要注意的地方:

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);
        }
    }
}





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章