官方文档相关部分:提升性能
抓取策略指多表关联查询的时候,Hibernate会发出多条sql进行查询,但如果设置了抓取策略,将多条SQL变成一条SQL执行,实际上就是使用join等关联查询。
如在一对多即示例Classes与Student的关联查询中:
代码:
- public void testLoad(){
- Session session = null;
- try {
- session = HibernateUtil.getSession();
- session.beginTransaction();
- //从classes得到student
- Classes cls = (Classes)session.load(Classes.class, 1);
- System.out.println(cls.getName());
- Set set = cls.getStudents();
- for (Iterator iterator = set.iterator(); iterator.hasNext();) {
- Student object = (Student) iterator.next();
- System.out.println(object.getName());
- }
- HibernateUtil.commit(session);
- } catch (Exception e) {
- HibernateUtil.roolback(session);
- }finally{
- HibernateUtil.close(session);
- }
- }
可以看到上面要打印出classes.name和classes.student.name那么默认情况下输出的SQL是:
- Hibernate: select classes0_.id as id0_0_, classes0_.name as name0_0_ from t_classes classes0_ where classes0_.id=?
- classes1
- Hibernate: select students0_.class_id as class3_0_1_, students0_.id as id1_, students0_.id as id1_0_, students0_.name as name1_0_, students0_.class_id as class3_1_0_ from t_student students0_ where students0_.class_id=?
- stu4
- stu2
- stu1
- stu0
- stu3
而我们可以使用抓取策略:
更改Classes.hbm.xml.在set中增加fetch属性
- <hibernate-mapping package="com.lwf.hibernate.pojo">
- <class name="Classes" table="t_classes">
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="name"/>
- <set name="students" fetch="join">
- <key column="class_id"/>
- <one-to-many class="Student"/>
- </set>
- </class>
- </hibernate-mapping>
更改完后再测试上面的代码,只发出一条sql,结果为:
- Hibernate: select classes0_.id as id0_1_, classes0_.name as name0_1_, students1_.class_id as class3_0_3_, students1_.id as id3_, students1_.id as id1_0_, students1_.name as name1_0_, students1_.class_id as class3_1_0_ from t_classes classes0_ left outer join t_student students1_ on classes0_.id=students1_.class_id where classes0_.id=?
- classes1
- stu2
- stu4
- stu1
- stu3
- stu0
显然上面比较使用抓取策略后执行的SQL语句少了,性能也跟着提高了。。
上面我们是从Classes端得到Student所以在Classes.hbm.xml中的set里面加了fetch属性。
那么如果我们从Student端得到Classes要使用fetch怎么做呢?
测试方法:
- public void testLoad1(){
- Session session = null;
- try {
- session = HibernateUtil.getSession();
- session.beginTransaction();
- Student s = (Student)session.load(Student.class, 1);
- System.out.println(s.getName());
- System.out.println(s.getClasses().getName());
- HibernateUtil.commit(session);
- } catch (Exception e) {
- HibernateUtil.roolback(session);
- }finally{
- HibernateUtil.close(session);
- }
- }
默认情况下执行:发出两条SQL
- Hibernate: select student0_.id as id1_0_, student0_.name as name1_0_, student0_.class_id as class3_1_0_ from t_student student0_ where student0_.id=?
- stu0
- Hibernate: select classes0_.id as id0_0_, classes0_.name as name0_0_ from t_classes classes0_ where classes0_.id=?
- classes1
而要执行抓取策略,更改Student.hbm.xml的many-to-one
- <?xml version="1.0"?>
- <!DOCTYPE hibernate-mapping PUBLIC
- "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
- "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
- <hibernate-mapping package="com.lwf.hibernate.pojo">
- <class name="Student" table="t_student">
- <!-- <cache usage="read-only"/> -->
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="name"/>
- <many-to-one name="classes" column="class_id" fetch="join"></many-to-one>
- </class>
- </hibernate-mapping>
上面我们增加了fetch="join"属性
结果:
- Hibernate: select student0_.id as id1_1_, student0_.name as name1_1_, student0_.class_id as class3_1_1_, classes1_.id as id0_0_, classes1_.name as name0_0_ from t_student student0_ left outer join t_classes classes1_ on student0_.class_id=classes1_.id where student0_.id=?
- stu0
- classes1
现在只执行一条SQL了。。。
需要提示的是其实默认情况下fetch="select"
在Classes与Student一对多映射中,我们将Set里面的fetch属性设置为subselect来实现子查询。
如下示例
首先看一下set中fetch="select"即默认情况下:
生成数据类:
- package com.bjsxt.hibernate;
- import org.hibernate.Session;
- public class InitData {
- public static void main(String[] args) {
- Session session = HibernateUtils.getSession();
- try {
- session.beginTransaction();
- for(int i=0; i<10; i++){
- Classes classes = new Classes();
- classes.setName("班级"+i);
- session.save(classes);
- for(int j=0; j<10; j++){
- Student student = new Student();
- student.setName("班级"+i+"的学生"+j);
- //在内存中建立由student指向classes的引用
- student.setClasses(classes);
- session.save(student);
- }
- }
- session.getTransaction().commit();
- } catch (Exception e) {
- e.printStackTrace();
- session.getTransaction().rollback();
- } finally{
- HibernateUtils.closeSession(session);
- }
- }
- }
测试类:
- package com.bjsxt.hibernate;
- import java.io.Serializable;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Set;
- import org.hibernate.Session;
- import junit.framework.TestCase;
- public class FetchTest extends TestCase {
- public void testFetch1() {
- Session session = null;
- try {
- session = HibernateUtils.getSession();
- Classes classes = (Classes)session.load(Classes.class, 1);
- System.out.println("班级:" + classes.getName());
- Set students = classes.getStudents();
- for (Iterator iter = students.iterator(); iter.hasNext();) {
- Student student = (Student)iter.next();
- System.out.println(student.getName());
- }
- }catch(Exception e) {
- e.printStackTrace();
- }finally {
- HibernateUtils.closeSession(session);
- }
- }
- public void testFetch2() {
- Session session = null;
- try {
- session = HibernateUtils.getSession();
- List Classes = session.createQuery("select c from Classes c where c.id in(1, 2, 3)").list();
- for (Iterator iter = Classes.iterator(); iter.hasNext();) {
- Classes cls = (Classes)iter.next();
- System.out.println("班级:" + cls.getName());
- for (Iterator iter1 = cls.getStudents().iterator(); iter1.hasNext();) {
- Student student = (Student)iter1.next();
- System.out.println(student.getName());
- }
- }
- }catch(Exception e) {
- e.printStackTrace();
- }finally {
- HibernateUtils.closeSession(session);
- }
- }
- }
分别列出方法一与方法二的测试结果:
- Hibernate: select classes0_.id as id0_0_, classes0_.name as name0_0_ from t_classes classes0_ where classes0_.id=?
- 班级:班级0
- Hibernate: select students0_.classid as classid0_1_, students0_.id as id1_, students0_.id as id1_0_, students0_.name as name1_0_, students0_.classid as classid1_0_ from t_student students0_ where students0_.classid=?
- 班级0的学生8
- 班级0的学生1
- 班级0的学生7
- 班级0的学生5
- 班级0的学生3
- 班级0的学生4
- 班级0的学生0
- 班级0的学生6
- 班级0的学生9
- 班级0的学生2
- Hibernate: select classes0_.id as id0_, classes0_.name as name0_ from t_classes classes0_ where classes0_.id in (1 , 2 , 3)
- 班级:班级0
- Hibernate: select students0_.classid as classid0_1_, students0_.id as id1_, students0_.id as id1_0_, students0_.name as name1_0_, students0_.classid as classid1_0_ from t_student students0_ where students0_.classid=?
- 班级0的学生7
- 班级0的学生2
- 班级0的学生3
- 班级0的学生1
- 班级0的学生0
- 班级0的学生9
- 班级0的学生4
- 班级0的学生8
- 班级0的学生6
- 班级0的学生5
- 班级:班级1
- Hibernate: select students0_.classid as classid0_1_, students0_.id as id1_, students0_.id as id1_0_, students0_.name as name1_0_, students0_.classid as classid1_0_ from t_student students0_ where students0_.classid=?
- 班级1的学生9
- 班级1的学生0
- 班级1的学生6
- 班级1的学生8
- 班级1的学生7
- 班级1的学生2
- 班级1的学生5
- 班级1的学生3
- 班级1的学生1
- 班级1的学生4
- 班级:班级2
- Hibernate: select students0_.classid as classid0_1_, students0_.id as id1_, students0_.id as id1_0_, students0_.name as name1_0_, students0_.classid as classid1_0_ from t_student students0_ where students0_.classid=?
- 班级2的学生6
- 班级2的学生9
- 班级2的学生3
- 班级2的学生1
- 班级2的学生2
- 班级2的学生7
- 班级2的学生8
- 班级2的学生4
- 班级2的学生0
- 班级2的学生5
下面更改fetch="subselect"
- <?xml version="1.0"?>
- <!DOCTYPE hibernate-mapping PUBLIC
- "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
- "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
- <hibernate-mapping>
- <class name="com.bjsxt.hibernate.Classes" table="t_classes">
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="name"/>
- <!--
- <set name="students" cascade="all" order-by="id">
- -->
- <set name="students" inverse="true" fetch="subselect">
- <key column="classid"/>
- <one-to-many class="com.bjsxt.hibernate.Student"/>
- </set>
- </class>
- </hibernate-mapping>
同样,将两个方法的测试结果列出如下:
- Hibernate: select classes0_.id as id0_0_, classes0_.name as name0_0_ from t_classes classes0_ where classes0_.id=?
- 班级:班级0
- Hibernate: select students0_.classid as classid0_1_, students0_.id as id1_, students0_.id as id1_0_, students0_.name as name1_0_, students0_.classid as classid1_0_ from t_student students0_ where students0_.classid=?
- 班级0的学生3
- 班级0的学生1
- 班级0的学生9
- 班级0的学生7
- 班级0的学生5
- 班级0的学生0
- 班级0的学生6
- 班级0的学生2
- 班级0的学生8
- 班级0的学生4
- Hibernate: select classes0_.id as id0_, classes0_.name as name0_ from t_classes classes0_ where classes0_.id in (1 , 2 , 3)
- 班级:班级0
- Hibernate: select students0_.classid as classid0_1_, students0_.id as id1_, students0_.id as id1_0_, students0_.name as name1_0_, students0_.classid as classid1_0_ from t_student students0_ where students0_.classid in (select classes0_.id from t_classes classes0_ where classes0_.id in (1 , 2 , 3))
- 班级0的学生5
- 班级0的学生1
- 班级0的学生0
- 班级0的学生8
- 班级0的学生7
- 班级0的学生2
- 班级0的学生9
- 班级0的学生6
- 班级0的学生4
- 班级0的学生3
- 班级:班级1
- 班级1的学生5
- 班级1的学生1
- 班级1的学生0
- 班级1的学生7
- 班级1的学生4
- 班级1的学生9
- 班级1的学生8
- 班级1的学生3
- 班级1的学生6
- 班级1的学生2
- 班级:班级2
- 班级2的学生0
- 班级2的学生5
- 班级2的学生1
- 班级2的学生8
- 班级2的学生6
- 班级2的学生9
- 班级2的学生4
- 班级2的学生7
- 班级2的学生3
- 班级2的学生2
比较两次测试的结果我们发现,方法二的输出结果不同,当fetch="subselect"的时候,方法二进行了子查询
如:
- Hibernate: select students0_.classid as classid0_1_, students0_.id as id1_, students0_.id as id1_0_, students0_.name as name1_0_, students0_.classid as classid1_0_ from t_student students0_ where students0_.classid in (select classes0_.id from t_classes classes0_ where classes0_.id in (1 , 2 , 3))