首先要搞清楚:
抓取就是查询嘛,查询关联对象.可以决定如何抓取关联对象.fetch=slect的意思是另外发送一条语句加载其关联对象及集合.
主要有单端代理即many-to-one这种类型的,集合代理,批量抓取
单端代理的批量抓取:fetch有三,一种是不填,一种是填select,还有一种是填join
1.保持默认,同fetch="select",如:
<many-to-one name="classes" column="classesid" fetch="select"/>,fetch="select",另外发送一条select语句加载当前对象的关联对象或集合
fetch为select或join不影响hql,它影响的是load,get方法,此时lazy是不会失效的
//会发送sql语句的
Student student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
//会发送sql语句的
Classes classes = student.getClasses();
System.out.println("classes.name=" + classes.getName());
2.设置fetch="join",如:<many-to-one name="classes" column="classesid" fetch="join"/>,fetch="join",hibernate会通过一个select语句连接(内联/外联)抓取其关联对象或集合
fetch="join",那么lazy失效
fetch="join",只影响get和load,对hql没有影响
Student student = (Student)session.load(Student.class, 1);
//发送一条sql语句:select student0_.id as id1_1_, student0_.name as name1_1_, student0_.classesid as classesid1_1_, classes1_.id as id0_0_, classes1_.name as //name0_0_ from t_student student0_ inner join t_classes classes1_ on student0_.classesid=classes1_.id where student0_.id=?
System.out.println("student.name=" + student.getName());
Classes classes = student.getClasses();
//这里不会发Sql语句了
System.out.println("classes.name=" + classes.getName());
采用hql:
//会发送一条语句
Student student = (Student)session.createQuery("select s from Student s join s.classes where s.id=1").list().get(0);
System.out.println("student.name=" + student.getName());
Classes classes = student.getClasses();
//会发送一条语句
System.out.println("classes.name=" + classes.getName());
如果用以下的hql语句则不会发送两条语句:这叫预加载,注意预加载的时候fetch不能fetch他的集合的,这样会出错的,而要反过来.
Student student = (Student)session.createQuery("select s from Student s join fetch s.classes where s.id=1").list().get(0);
集合代理的批量抓取:fetch的取值有四,一种是默认的不填写,一种是fetch=select,一种是fetch=join,还有一种是fetch=
1.保持默认,同fetch="select",如:
<set name="students" order-by="id" inverse="true" cascade="all" fetch="select">
fetch="select",另外发送一条select语句加载当前对象的关联对象或集合,如下会发送两条sql语句
Classes classes = (Classes)session.load(Classes.class, 1);
System.out.println(classes.getName());
for (Iterator iter=classes.getStudents().iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
2.设置fetch="join",如:<set name="students" order-by="id" inverse="true" cascade="all" fetch="join">
fetch="join",hibernate会通过一个select语句连接(内联/外联)抓取其关联对象或集合
fetch="join",那么lazy失效
fetch="join",只影响get和load,对hql没有影响
3.设置fetch="subselect",如:
<set name="students" order-by="id" inverse="true" cascade="all" fetch="subselect">
fetch="subselect",另外发送一条select语句抓取在前面查询到的所有实体的关联集合
fetch="subselect",会影响hql查询,会发送一条子查询语句.
Classes classes = (Classes)session.load(Classes.class, 1);
//发送一条查询实体的sql语句
System.out.println(classes.getName());
//发送一条查询前面查询到的classes实体所关联的集合
for (Iterator iter=classes.getStudents().iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
以下用hql语句:
List classesList = session.createQuery("select c from Classes c where c.id in(1, 2, 3)").list();
for (Iterator iter1=classesList.iterator(); iter1.hasNext();) {
Classes classes = (Classes)iter1.next();
System.out.println(classes.getName());
//这里会发出一条子查询的语句
//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)) //order by students0_.id
for (Iterator iter=classes.getStudents().iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
}
batch-size的用法,即是批量抓取
1.batch-size在<class>的应用
batch-size属性,可以批量加载实体类,参见Classes.hbm.xml,<class name="com.bjpowernode.hibernate.Classes" table="t_classes" batch-size="10">,加载班级的时候成10个一批进行加载.测试如下:
先不设置batch-size="10"
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.getName());
System.out.println(student.getClasses().getName());
}
那么这样会发生N+1的问题,
查询10个学生显示到列表中:
* 首先会发出查询学生的sql语句
* 然后会发出根据班级id查询班级的sql语句
这样就会导致N+1问题,也就是发出了N+1条语句,会严重影响性能
解决方法其实有二种:一种是采用使用fetch预抓取Classes,如下:
List students = session.createQuery("select s from Student s join fetch s.classes where s.id in(:ids)")
.setParameterList("ids", new Object[]{1, 11, 21, 31, 41, 51, 61, 71, 81, 91})
.list();
一种是采用批量抓取策略:<class name="com.bjpowernode.hibernate.Classes" table="t_classes" batch-size="10">
2.batch-size在集合上的应用
batch-size属性,可以批量加载实体类,<set name="students" order-by="id" inverse="true" cascade="all" batch-size="3">
List classesList = session.createQuery("select c from Classes c").list();
for (Iterator iter1=classesList.iterator(); iter1.hasNext();) {
Classes classes = (Classes)iter1.next();
System.out.println(classes.getName());
for (Iterator iter=classes.getStudents().iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
}
生成的语句是这样的:
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 (?, ?, ?) order by students0_.id
如果不设置batch-size="3",那么会发出10条语句的,生成的语句如下:
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=? order by students0_.id