J2EE系列之Hibernate4学习笔记(十五)--hibernate检索策略

这里讲几个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语句,先查询班级信息,再查询各个班级的学生信息。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值