1. 程式人生 > >【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三個屬性的作用和使用方法