这里讲几个hibernate中常用的属性,能够用来优化数据库的检索。
1.新建工程:Hibernate09;
2.新建学生类Student:
package com.test.model;
public class Student {
private long id;
private String name;
private Class c;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Class getC() {
return c;
}
public void setC(Class c) {
this.c = c;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + "]";
}
}
3.新建班级类Class:
package com.test.model;
import java.util.HashSet;
import java.util.Set;
public class Class {
private long id;
private String name;
private Set<Student> students = new HashSet<Student>();
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Student> getStudents() {
return students;
}
public void setStudents(Set<Student> students) {
this.students = students;
}
}
4.写映射Student类的文件:
<?xml version="1.0"?>
<!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.test.model">
<class name="Student" table="t_student">
<id name="id" column="stuId">
<generator class="native"></generator>
</id>
<property name="name" column="stuName"></property>
<many-to-one name="c" column="classId" class="com.test.model.Class" cascade="save-update"></many-to-one>
</class>
</hibernate-mapping>
5.写Class类的映射文件:
<?xml version="1.0"?>
<!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.test.model">
<class name="Class" table="t_class">
<id name="id" column="classId">
<generator class="native"></generator>
</id>
<property name="name" column="className"></property>
<set name="students" cascade="delete">
<key column="classId"></key> <!-- 对应的外键 -->
<one-to-many class="com.test.model.Student"/>
</set>
</class>
</hibernate-mapping>
这里是一个一对多映射实例。
一、这里讲一个Lazy属性,其使用方法如下:
Lazy:true (默认) 延迟检索;set 端一对多
Lazy:false 立即检索;set 端一对多
Lazy:extra 增强延迟检索; set 端一对多
Lazy:proxy(默认) 延迟检索;many-to-one 多对一
Lazy:no-proxy 无代理延迟检索;many-to-one 多对一(需要编译时字节码增强)
6.写测试方法:
package com.test.service;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.test.model.Class;
import com.test.model.Student;
import com.test.util.HibernateUtil;
public class StudentTest {
private SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); // 获取Session工厂
private Session session;
@Before
public void setUp() throws Exception {
session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
}
@After
public void tearDown() throws Exception {
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
@Test
public void testLazy1() {
}
}
运行测试方法,看一下生成的表结构:
现在往生成的两个表中添加测试数据如下:
lazy属性写在set端,如果不写这个属性的值的话,其默认值是true。修改一下class的映射文件,在set端添加lazy属性值设为true:
<set name="students" cascade="delete" lazy="true">
<key column="classId"></key> <!-- 对应的外键 -->
<one-to-many class="com.test.model.Student"/>
</set>
lazy值为true是延迟加载策略,只有在用到的时候才会去加载数据库表中的数据。修改测试方法为:
@Test
public void testLazy1() {
Class c = (Class) session.get(Class.class, Long.valueOf(1));
}
运行该测试方法,控制台输出的sql语句为:
这里只执行了一条sql语句,那就是查询主键为1的班级。这里并没有查询这个班级里面的学生信息。
修改一下测试方法:
@Test
public void testLazy1() {
Class c = (Class) session.get(Class.class, Long.valueOf(1));
Set<Student> studentList = (Set<Student>)c.getStudents();
studentList.iterator();
}
运行测试方法:
上面的测试代码中用到了班级中的学生信息,才会调用查询sql语句去查询这个班级中的学生。
修改lazy的值为false,就变成了立即检索:
<set name="students" cascade="delete" lazy="false">
<key column="classId"></key> <!-- 对应的外键 -->
<one-to-many class="com.test.model.Student"/>
</set>
修改测试函数为:
@Test
public void testLazy1() {
Class c = (Class) session.get(Class.class, Long.valueOf(1));
}
运行这个测试方法:
可以看到,这里在查询班级信息的时候把班级的学生信息也进行了查询。
看一种新的情况,把lazy的值改成true,并修改测试函数为:
@Test
public void testLazy1() {
Class c = (Class) session.get(Class.class, Long.valueOf(1));
Set<Student> studentList = (Set<Student>)c.getStudents();
System.out.println(studentList.size());
}
运行测试函数:
我们这里用到了学生信息,所以执行了两条sql语句,一个是查询主键为1的班级,另一个是查询这个班级中的所有学生。这里,我们的测试方法中只是要求输出学生的数量,并没有用到学生的其他信息,而这里执行的sql语句却是查询了班级中的所有学生的所有信息。与我们只要学生数量而言,上面执行的sql虽然能够满足要求,但是却做了很多其他的无用功。这里可以把lazy的值设置为extra,它的一种增强延迟策略。
<set name="students" cascade="delete" lazy="extra">
<key column="classId"></key> <!-- 对应的外键 -->
<one-to-many class="com.test.model.Student"/>
</set>
运行上述测试方法:
可以看到,这里执行的第二条sql语句仅仅是查询该班级中学生的数量,这样的话就提高了查询效率。
lazy还可以取值为proxy,取这个值是要在many-to-one这一端使用。它的作用也是延迟检索。
修改Student类的映射文件:
<class name="Student" table="t_student">
<id name="id" column="stuId">
<generator class="native"></generator>
</id>
<property name="name" column="stuName"></property>
<many-to-one name="c" column="classId" class="com.test.model.Class" cascade="save-update" lazy="proxy"></many-to-one>
</class>
写测试方法:
@Test
public void testLazy2() {
Student s = (Student) session.get(Student.class, Long.valueOf(1));
s.getC().getName();
}
在测试方法中第二条语句处设置断点,运行到断点处:
可以看到只执行了一条sql语句为查询主键为1的学生信息。但是这个学生对象的班级信息却有数据,这是一个代理类,实际上它是一个空对象,没有任何数据。继续运行下一行代码,会利用这个代理类去查询班级信息。
二、检索策略属性batch-size
这个属性的作用是实现批量延迟检索或者批量立即检索。
看如下测试方法:
@Test
public void testBatch1() {
List<Class> classList = session.createQuery("from Class").list();
Iterator it = classList.iterator();
Class c1 = (Class) it.next();
Class c2 = (Class) it.next();
Class c3 = (Class) it.next();
c1.getStudents().iterator();
c2.getStudents().iterator();
c3.getStudents().iterator();
}
这里一共执行了4条sql语句,查询所有的班级信息,然后分别查询各个班级的学生信息。加入有100个班级时,这种情况就要执行100多条sql语句,这样的话效率会非常低。这时可以使用batch-size属性。修改class类的映射文件:
<set name="students" cascade="delete" lazy="true" batch-size="3">
<key column="classId"></key> <!-- 对应的外键 -->
<one-to-many class="com.test.model.Student"/>
</set>
可以看到这里只运行了2条sql语句。第二条语句直接查询3个班级信息。
上面的配置中lazy的值为true,属于延迟批量检索。把lazy的值改为false,就是立即延迟检索。修改映射文件:
<set name="students" cascade="delete" lazy="false" batch-size="3">
<key column="classId"></key> <!-- 对应的外键 -->
<one-to-many class="com.test.model.Student"/>
</set>
新建测试方法:
@Test
public void testBatch2() {
List<Class> classList = session.createQuery("from Class").list();
}
运行这个测试方法:
可以看到这里也是运行了2条sql语句,直接把班级中的学生信息给获取了。
三.检索策略属性Fetch
1,Fetch:select(默认) 查询方式;
2,Fetch:subselect 子查询方式;
3,Fetch:join 迫切左外连接查询方式;
修改Class映射文件:
<set name="students" cascade="delete" lazy="false" batch-size="3" fetch="select">
<key column="classId"></key> <!-- 对应的外键 -->
<one-to-many class="com.test.model.Student"/>
</set>
写测试函数:
@Test
public void testFetch1() {
List<Class> classList = session.createQuery("from Class").list();
}
运行这个测试函数,看一下执行的sql语句为:
这里是执行的第2条sql语句的末尾部分,这里是批量查询班级中的学生信息。
修改映射文件为:把fetch的值改为subselect
<set name="students" cascade="delete" lazy="false" batch-size="3" fetch="subselect">
<key column="classId"></key> <!-- 对应的外键 -->
<one-to-many class="com.test.model.Student"/>
</set>
运行上述测试方法,执行的sql语句为:
可以看到这里查询学生的sql语句与上一个是不同的。这里使用的是子查询,对于数据量很大的查询可以使用子查询,效率要比全部查询高很大,当数据量很小时无所谓。
Fetch还可以取值为join,是左外连接查询方式。这种查询方式对于使用hql语句的查询是无效的。比如修改映射文件:
<set name="students" cascade="delete" lazy="false" batch-size="3" fetch="join">
<key column="classId"></key> <!-- 对应的外键 -->
<one-to-many class="com.test.model.Student"/>
</set>
测试方法还是上面的testFetch1,这个方法里面使用的是hql语句查询班级信息。运行这个测试方法:
这里执行的sql语句和fetch取值为select时相同。
修改测试方法:
@Test
public void testFetch2() {
Class c = (Class) session.get(Class.class, Long.valueOf(1));
}
运行这个测试方法:
可以看到这里只执行了一条sql语句,这条sql语句使用了左外连接查询,把班级和学生信息都进行了查询。
把fetch的值改回select,运行这个测试方法:
这里执行了两条sql语句,先查询班级信息,再查询各个班级的学生信息。