21、抓取策略(hibernate笔记)

一、简介

抓取策略是指:当应用程序需要在关联关系间进行导航的时候,hibernate如何获取关联对象的策略。可以在O/R映射的元数据中声明,也可以在特定的hql或条件查询中重载声明。有几种策略:连接抓取(Join)、查询抓取(Select)、子查询抓取(subselect)、批量抓取(batch)。其中查询抓取是默认的。

注意:下面的示例中所使用的实体和映射和前面查询缓存中是一样的。

二、单端代理的批量抓取

2.1使用select策略(工程hibernate_fetch_1

由于默认是Select抓取策略,所以我们可以保持默认,或者也可以在Student.hbm.xml中进行配置:

测试:
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);
        }
    }   
}

说明:可以看到在运行此程序时会发出两条sql语句分别去查询学生名字和班级名字。

2.2使用join策略(工程hibernate_fetch_2

Student.hbm.xml中进行配置:

测试:
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);
        }
    }   
}

说明:这里就只会发出一条sql语句,使用一个外连接将学生和班级都查询出来了,当然lazy就相当于失效了。

三、集合代理的批量抓取

3.1使用默认策略(工程hibernate_fetch_3

Classes.hbm.xml中:表示支持lazy,会发出两次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);
        }
    }   
}

说明:同样在默认情况下是支持lazy的,会发出两次sql语句查询。

3.2使用join策略(工程hibernate_fetch_4

Classes.hbm.xml中:
表示在发出第一条语句的时候就会将关联的实体对象查出来,之后将不再发出sql去查询关联的实体对象。

测试的例子和上面一样。同样使用join策略就不会再次发出sql语句了。

3.3使用subselect策略(工程hibernate_fetch_5

Classes.hbm.xml中:

如果默认需要查询三个关联实体对象则在默认情况下会发出三条sql语句去查询关联对象,但是如果设置了subselect则只会发出一条sql语句将三个实体对象都查出来。

测试使用的例子和上面一样。而实际运行结果和默认的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);
        }
    }

说明:如果抓取策略是默认的,先发出一条sql语句查询出班级,然后再根据id依次发出3条sql语句去查班级里面的学生。当使用subselect策略时,也是先发出一条sql语句查询出班级,但是之后则只会发出一条sql语句将前面查询到的班级中的学生都查询出来。

四、抓取策略中的批量操作

4.1在实体类中使用(工程hibernate_fetch_6

Classes.hbm.xml中:
这里设置批量操作值为5,表示每次查询5条结果集。

测试:
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);
        }
    }   
}

说明:默认是首先发出sql去查询学生,然后一次发出多条sql分别查询每个学生的班级,如果我们使用批量操作,那么会一次查询多条数据,比如这里设置批量值为5,那么查询10个学生的班级,那么只会发出两条sql而不是默认的10条。

4.2在集合中使用(工程hibernate_fetch_7

Classes.hbm.xml中:
这里设置批量操作值为5,表示每次查询5条结果集。

测试:
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);
        }       
    }   
}

说明:本来默认是发出N条sql语句查询的,但是这里我们设置了集合的批量操作,那么每次就会查询出多条记录,发出的sql语句也就相应的减少。

五、批量更新

  • jdbc fetch size
    每次取多少数据,需要jdbc和底层数据库的支持。不会一次性把全部数据读入内存,而是按照一定的数量来批量读取相应的数据。建议值是50,在hibernate.cfg.xml中:
    50

  • jdbc batch size
    批量更新,建议取值为30,在hibernate.cfg.xml中:
    30

你可能感兴趣的:(21、抓取策略(hibernate笔记))