首页 > 代码库 > 【SSH三大框架】Hibernate基础第十二篇:load()懒加载分析以及一对一、一对多、多对一、多对多懒加载的分析

【SSH三大框架】Hibernate基础第十二篇:load()懒加载分析以及一对一、一对多、多对一、多对多懒加载的分析

一、懒加载的定义:

懒加载:在WEB应用程序中,经常会需要查询数据库,系统的响应速度在很大程度上是与数据库交互的响应。因此,如果能够优化与数据库的交互速度,则能够大大提高WEB应用的响应速度。

例如:当有一个Student类和一个Teacher类。当我们加载一个学生的所有信息,包括:学号,姓名等属性后,此时Student类中的Teacher类型的属性为null,当我们需要知道这个Student对应的Teacher属性的时候,我们才去加载这个Teacher对象。

如果,我们只需要知道学生信息,我们只需要查询一次数据库中的Stduent表,并且不需要根据外键查询Teacher表

如果,我们还需要知道老师信息,我们在查询Student表之后,还需要再做一次查询,查询Teacher表。


二、懒加载的原理及分析:

User.java

public class User {
 private int id;
 private String name;
 private Date  birthday;

//省略了setter和getter方法

}
User.hbm.xml:

<?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="cn.itcast.hibernate.domain">

	<class name="User" table="uuser" lazy="true">
		<id name="id">
			<generator class="native"/> 
		</id>
		<property name="name" />
		<property name="birthday" />
	</class>
</hibernate-mapping>
这是User的实体类和映射文件。


我们来写一个测试的类:

public class Base {
	public static void main(String[] args) {
		User user = new User("lipenglong", new Date());
		addUser(user);
		User u = getUser(user.getId());
		System.out.println("Birthday:"+u.getBirthday());
	}
	static void addUser(User user) {
		Configuration cfg = new Configuration();  
                cfg.configure();  
                SessionFactory sf = cfg.buildSessionFactory();  
                Session s = sf.openSession();  
                Transaction tx =  s.beginTransaction();      
                s.save(user);  
                tx.commit();  
                s.close();  
	}
	static User getUser(int id){
		Session s = null;
		User u = null;
		try{
			s = HibernateUtil.getSession();
			u = (User) s.load(User.class,id);
		}finally{
			if(s!=null){
				s.close();
			}
		}
		return u;
	}
}
可以看到,我们首先是新增了一个user,然后要根据user的id在getUser(int id)方法中load了一个User对象,然后返回。

再在main函数中调用这个对象的birthday属性。

但是这样子会造成一个错误:

这是延迟加载的异常:在session对象打开的时候,我们延迟加载了一个对象,然后我们把session关闭了。在main函数中想使用这个对象,就会报出这个异常。

有一下的几种解决方法:

1、换成get(class,id)方法

换成get(class,id)方法之后,我们会在getUser(int id)函数执行的时候,就会生成一条sql语句:

2、在load(class,id)方法之后,也就是session关闭之前,初始化这个对象(增加:Hibernate.initialize(u);语句)。

在增加这条语句之后,我们就可以在main函数中调用这个对象的方法了:


我们可以把Birthday属性打印出来了。

3、在load(class,id)方法之后,也就是session关闭之前,调用这个对象的方法,就相当于初始化了(增加:u.getName();语句或者u.getBirthday();)

static User getUser(int id){
		Session s = null;
		User u = null;
		try{
			s = HibernateUtil.getSession();
			u = (User) s.load(User.class,id);
			u.getName();
			//u.getBirthday();
		}finally{
			if(s!=null){
				s.close();
			}
		}
		return u;
	}
我们修改之后,就可以在main函数中调用这个对象的方法了。


三、一对一懒加载的原理及分析:

在一对一的时候,查询主对象时默认不是懒加载。即:查询主对象的时候也会把从对象查询出来。

需要把主对象配制成lazy="true" constrained="true"  fetch="select"。此时查询主对象的时候就不会查询从对象,从而实现懒加载。

一对一的时候,查询从对象默认是懒加载。即:查询从对象的时候不会把主对象查询出来。而是查询出来的是主对象的代理对象。

下边,我们用Person和IdCard举例子:

Person.java:

public class Person {
	private int id;
	private String name;
	private IdCard idCard;
	
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public IdCard getIdCard() {
		return idCard;
	}
	public void setIdCard(IdCard idCard) {
		this.idCard = idCard;
	}
	
}
Person.hbm.xml:

<?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="cn.itcast.hibernate.domain">

	<class name="Person" table="person">
		<id name="id">
			<generator class="native"/> 
		</id>
		<property name="name" />
		<one-to-one name="idCard"/>
	</class>
</hibernate-mapping>
IdCard.java:

public class IdCard {
	private int id;
	private Date userfulLift;
	private Person person;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public Date getUserfulLift() {
		return userfulLift;
	}
	public void setUserfulLift(Date userfulLift) {
		this.userfulLift = userfulLift;
	}
	public Person getPerson() {
		return person;
	}
	public void setPerson(Person person) {
		this.person = person;
	}
	
}


IdCard.hbm.xml:

<?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="cn.itcast.hibernate.domain">

	<class name="IdCard" table="id_card" >
		<id name="id">
			<generator class="foreign">
				<param name="property">person</param>
			</generator> 
		</id>
		<property name="userfulLift" column="useful_life" />
		<one-to-one name="person" constrained="true"/>
	</class>
</hibernate-mapping>


然后,我们写一个测试类:

package cn.itcast.hibernate;

import java.util.Date;

import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.Transaction;

import cn.itcast.hibernate.domain.IdCard;
import cn.itcast.hibernate.domain.Person;

public class One2One {
	public static void main(String[] args) {
		add();
		System.out.println("——————————————————");
		IdCard idCard = queryIdCard(1);
		
		System.out.println("————————————————————");
		Person p = queryPerson(1);
	}
	static Person add(){
		Session s = null;
		Transaction tx = null;
		try{
			s = HibernateUtil.getSession();
			IdCard idCard = new IdCard();
			idCard.setUserfulLift(new Date());
			
			Person p = new Person();
			p.setName("p1");
			p.setIdCard(idCard);
			idCard.setPerson(p);
			
			tx =s.beginTransaction();
			s.save(p);
			s.save(idCard);
			tx.commit();
			return p;
		}finally{
			if(s!=null){
				s.close();
			}
		}
	}
	static IdCard queryIdCard(int id){
		Session s = null;
		Transaction tx = null;
		try{
			s = HibernateUtil.getSession();
			tx = s.beginTransaction();
			IdCard idCard = (IdCard) s.get(IdCard.class, id);
			tx.commit();
			return idCard;
		}finally{
			if(s!=null){
				s.close();
			}
		}
	}
	static Person queryPerson(int id){
		Session s = null;
		Transaction tx = null;
		try{
			s = HibernateUtil.getSession();
			tx = s.beginTransaction();
			Person p = (Person) s.get(Person.class, id);
			tx.commit();
			return p;
		}finally{
			if(s!=null){
				s.close();
			}
		}
	}
}
我们在main函数中,调用了两个方法,分别是查询从对象IdCard和查询主对象Person,我们可以看下控制台打印出来的sql语句:

可以看到,我们生成了两条insert语句和两条查询语句。第一条查询语句仅仅是查询从表id_card表,但是第二条查询语句会根据采用连接的方式先查询主表Person再查询从表id_card。

所以,在一对一的关联中,Hibernate默认从表有懒加载,但是主表没有。


四、多对一关联的懒加载:

多对一的时候,查询主对象时默认是懒加载。即:查询主对象的时候不会把从对象查询出来。

多对一的时候,查询从对象时默认是懒加载。即:查询从对象的时候不会把主对象查询出来。

五、多对多关联的懒加载:

同上所述,总是会出现懒加载的现象。



懒加载在Hibernate中很多地方是默认的,那么我们如何关闭懒加载呢?

请看下一篇:

【SSH三大框架】Hibernate基础第十三篇:lazy、constrained、fetch三个属性的作用和使用方法











【SSH三大框架】Hibernate基础第十二篇:load()懒加载分析以及一对一、一对多、多对一、多对多懒加载的分析