一魂角、簡介
抓取策略是指:當(dāng)應(yīng)用程序需要在關(guān)聯(lián)關(guān)系間進行導(dǎo)航的時候,hibernate如何獲取關(guān)聯(lián)對象的策略。可以在O/R映射的元數(shù)據(jù)中聲明识埋,也可以在特定的hql或條件查詢中重載聲明反浓。有幾種策略:連接抓取(Join)烘豌、查詢抓取(Select)、子查詢抓日醵琛(subselect)、批量抓扰贡摺(batch)憎茂。其中查詢抓取是默認(rèn)的。
注意:下面的示例中所使用的實體和映射和前面查詢緩存中是一樣的锤岸。
二竖幔、單端代理的批量抓取
2.1使用select策略(工程hibernate_fetch_1
)
由于默認(rèn)是Select抓取策略,所以我們可以保持默認(rèn)是偷,或者也可以在Student.hbm.xml
中進行配置:
<many-to-one name="classes" column="classesid" fetch="select"/>
測試:
FechTest .java
package cn.itcast.hibernate;
import org.hibernate.Session;
import junit.framework.TestCase;
public class FechTest extends TestCase {
public void testFetch1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
System.out.println("classes.name=" + student.getClasses().getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}
說明:可以看到在運行此程序時會發(fā)出兩條sql語句分別去查詢學(xué)生名字和班級名字拳氢。
2.2使用join策略(工程hibernate_fetch_2
)
在Student.hbm.xml
中進行配置:
<many-to-one name="classes" column="classesid" fetch="join"/>
測試:
FechTest .java
package cn.itcast.hibernate;
import org.hibernate.Session;
import junit.framework.TestCase;
public class FechTest extends TestCase {
public void testFetch1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
System.out.println("classes.name=" + student.getClasses().getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}
說明:這里就只會發(fā)出一條sql語句,使用一個外連接將學(xué)生和班級都查詢出來了晓猛,當(dāng)然lazy就相當(dāng)于失效了饿幅。
三、集合代理的批量抓取
3.1使用默認(rèn)策略(工程hibernate_fetch_3
)
在Classes.hbm.xml
中:<set name="students" inverse="true" cascade="all" fetch="select">
表示支持lazy戒职,會發(fā)出兩次sql語句去查詢栗恩。
測試:
FechTest.java
package cn.itcast.hibernate;
import java.util.Iterator;
import org.hibernate.Session;
import junit.framework.TestCase;
public class FechTest extends TestCase {
public void testFetch1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Classes classes = (Classes)session.load(Classes.class, 1);
System.out.println("classes.name=" + classes.getName());
for (Iterator iter=classes.getStudents().iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println("student.name=" + student.getName());
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}
說明:同樣在默認(rèn)情況下是支持lazy的,會發(fā)出兩次sql語句查詢洪燥。
3.2使用join策略(工程hibernate_fetch_4
)
在Classes.hbm.xml
中:<set name="students" inverse="true" cascade="all" fetch="join">
表示在發(fā)出第一條語句的時候就會將關(guān)聯(lián)的實體對象查出來磕秤,之后將不再發(fā)出sql去查詢關(guān)聯(lián)的實體對象。
測試的例子和上面一樣捧韵。同樣使用join策略就不會再次發(fā)出sql語句了市咆。
3.3使用subselect策略(工程hibernate_fetch_5
)
在Classes.hbm.xml
中:
<set name="students" inverse="true" cascade="all" fetch="subselect">
如果默認(rèn)需要查詢?nèi)齻€關(guān)聯(lián)實體對象則在默認(rèn)情況下會發(fā)出三條sql語句去查詢關(guān)聯(lián)對象,但是如果設(shè)置了subselect則只會發(fā)出一條sql語句將三個實體對象都查出來再来。
測試使用的例子和上面一樣蒙兰。而實際運行結(jié)果和默認(rèn)的select策略是一樣的磷瘤,沒有什么不同。但是這種策略會影響hql查詢搜变,看下面的測試用例:
/**
* Hibernate: select students0_.classesid as classesid1_, students0_.id as id1_,
* students0_.id as id1_0_, students0_.name as name1_0_,
* students0_.classesid as classesid1_0_
* from t_student students0_
* where students0_.classesid in (select classes0_.id from t_classes classes0_ where classes0_.id in (1 , 2 , 3))
*/
public void testFetch2() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
List classesList = session.createQuery("select c from Classes c where c.id in(1, 2, 3)").list();
for (Iterator iter=classesList.iterator(); iter.hasNext();) {
Classes classes = (Classes)iter.next();
System.out.println("classes.name=" + classes.getName());
for (Iterator iter1=classes.getStudents().iterator(); iter1.hasNext();) {
Student student = (Student)iter1.next();
System.out.println("student.name=" + student.getName());
}
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
說明:如果抓取策略是默認(rèn)的采缚,先發(fā)出一條sql語句查詢出班級,然后再根據(jù)id依次發(fā)出3條sql語句去查班級里面的學(xué)生挠他。當(dāng)使用subselect策略時扳抽,也是先發(fā)出一條sql語句查詢出班級,但是之后則只會發(fā)出一條sql語句將前面查詢到的班級中的學(xué)生都查詢出來殖侵。
四贸呢、抓取策略中的批量操作
4.1在實體類中使用(工程hibernate_fetch_6
)
在Classes.hbm.xml
中:
<class name="cn.itcast.hibernate.Classes" table="t_classes" batch-size="5">
這里設(shè)置批量操作值為5,表示每次查詢5條結(jié)果集拢军。
測試:
FechTest .java
package cn.itcast.hibernate;
import java.util.Iterator;
import java.util.List;
import org.hibernate.Session;
import junit.framework.TestCase;
public class FechTest extends TestCase {
public void testFetch1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
List students = session.createQuery("select s from Student s where s.id in(:ids)")
.setParameterList("ids", new Object[]{1, 11, 21, 31, 41, 51, 61, 71, 81, 91})
.list();
for (Iterator iter=students.iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println("student.name=" + student.getName());
System.out.println("classes.name=" + student.getClasses().getName());
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}
說明:默認(rèn)是首先發(fā)出sql去查詢學(xué)生楞陷,然后一次發(fā)出多條sql分別查詢每個學(xué)生的班級,如果我們使用批量操作茉唉,那么會一次查詢多條數(shù)據(jù)猜谚,比如這里設(shè)置批量值為5,那么查詢10個學(xué)生的班級赌渣,那么只會發(fā)出兩條sql而不是默認(rèn)的10條魏铅。
4.2在集合中使用(工程hibernate_fetch_7
)
在Classes.hbm.xml
中:
<set name="students" inverse="true" cascade="all" batch-size="5">
這里設(shè)置批量操作值為5,表示每次查詢5條結(jié)果集坚芜。
測試:
FechTest .java
package cn.itcast.hibernate;
import java.util.Iterator;
import java.util.List;
import org.hibernate.Session;
import junit.framework.TestCase;
public class FechTest extends TestCase {
public void testFetch1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
List classesList = session.createQuery("select c from Classes c").list();
for (Iterator iter=classesList.iterator(); iter.hasNext();) {
Classes classes = (Classes)iter.next();
System.out.println("classes.name=" + classes.getName());
for (Iterator iter1=classes.getStudents().iterator(); iter1.hasNext();) {
Student student = (Student)iter1.next();
System.out.println("student.name=" + student.getName());
}
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}
說明:本來默認(rèn)是發(fā)出N條sql語句查詢的览芳,但是這里我們設(shè)置了集合的批量操作,那么每次就會查詢出多條記錄鸿竖,發(fā)出的sql語句也就相應(yīng)的減少沧竟。
五、批量更新
jdbc fetch size
每次取多少數(shù)據(jù)缚忧,需要jdbc和底層數(shù)據(jù)庫的支持悟泵。不會一次性把全部數(shù)據(jù)讀入內(nèi)存,而是按照一定的數(shù)量來批量讀取相應(yīng)的數(shù)據(jù)闪水。建議值是50糕非,在hibernate.cfg.xml
中:
<property name="hibernate.jdbc.fetch_size">50</property>
jdbc batch size
批量更新,建議取值為30球榆,在hibernate.cfg.xml
中:
<property name="hibernate.jdbc.batch_size">30</property>