有幾個需要注意的地方:
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());
}
7 注意:多對一時候 從多端懶加載一對象(目的是爲了全部加載多端對象)在一端加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);
}
}
}