一、懶加載的定義:
懶加載:在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三個屬性的作用和使用方法