1. 程式人生 > >hibernate的優化-懶載入(lazy)----張國亮總結心得第二季

hibernate的優化-懶載入(lazy)----張國亮總結心得第二季

為了進一步優化Hibernate的效能,可以使用:

延遲載入技術、管理資料抓取策略、進行快取管理 等方面考慮來提高Hibernate的效能。

1. 延遲載入(load)

延遲載入(load)是Hibernate為提高程式執行效率而提供的一種機制,即只有真正使用該物件的資料時才會建立

延遲載入的過程通過代理(Proxy)機制來實現延遲載入。Hibernate從資料庫獲取某一個物件資料時、獲取某一個物件的集合屬性值時,或獲取某一個物件所關聯的另一個物件時,由於沒有使用該物件的資料(除識別符號外),Hibernate並不從資料庫載入真正的資料,而只是為該物件建立一個代理物件來代表這個物件,這個物件上的所有屬性都為預設值;只有在真正需要使用該物件的資料時才建立這個真正的物件,真正從資料庫中載入它的資料。

當呼叫Session上的load()方法載入一個實體時;當Session載入某個實體時,會對這個實體中的集合屬性值採用延遲載入;當Session載入某個實體時,會對這個實體所單端關聯的另一個實體物件採用延遲載入

關閉延遲載入:在載入單個實體時,可以使用get()方法。

對於實體中的集合屬性,可以在這個集合的(<set>,<bag>,<list>…)新增屬性lazy=“false”。單端關聯另一個實體物件時,可以在對映檔案中配置<one-to-one>,<many-to-one> 新增屬性lazy=“false”注意:one-to-one不能有constrained=true(產生的sql語句中顯示外來鍵),否則懶載入不起作用。

Hibernate中預設採用延遲載入的情況主要有以下幾種

• 當呼叫Session上的load()方法載入一個實體時會採用延遲載入。

• 當Session載入某個實體時,會對這個實體中的集合屬性值採用延遲載入。(one-to-many)

• 當Session載入某個實體時,會對這個實體所單端關聯(one-to-one, many-to-one)的另一個實體物件採用延遲載入。

• 第二種和第三種的區別是:第二種情況下取消延時載入的方法是在單方即有set屬性的一方的對映檔案的set標籤後設置懶載入的屬性lazy=“false”;第三種情況則是在多方即有many-to-one的一方的對映檔案中的many-to-one標籤後設置lazy=“false”。

能夠懶載入的物件都是被改寫過的代理物件,當相關聯的session沒有關閉時,訪問這些懶載入物件(代理物件)的屬性(getId和getClass除外)hibernate會初始化這些代理,或用Hibernate.initialize(proxy)來初始化代理物件;當相關聯的session關閉後,再訪問懶載入的物件將出現異常。

3. 抓取策略(fetch)

通過配置“抓取策略”來直接影響session的get()和load()方法的查詢效果。

單端關聯<many-to-one><one-to_one>上的抓取策略:

可以給單端關聯的對映元素新增fetch屬性。select:延遲載入; join:在同一條select語句使用內連線來獲得物件的資料和它關聯物件的資料,此時關聯物件的延遲載入失效。

集合屬性上的抓取策略:

select:延遲載入;join:在同一條select語句使用內連線來獲得對方的關聯集合。此時關聯集合上的lazy會失效。subselect:另外發送一條查詢語句或子查詢抓取。這個策略對HQL的查詢也起作用。

4. 延遲載入案例分析

情況一:單個實體呼叫load()方法取消懶載入

package com.hbsi.test;

import org.hibernate.Session;

import org.junit.Test;


import com.hbsi.domain.User;

import com.hbsi.utils.HibernateUtil;


publicclass TestLazy {


//測試get()方法;get()方法不管你有沒有用到,總會執行sql語句

@Test

publicvoid testGet(){


Session session = HibernateUtil.getSession();

User user = (User) session.get(User.class,1);

// System.out.println(user.getName());

HibernateUtil.close();

//這裡要注意的是:即使關閉了session,使user處於託管狀態,仍然可以可以使用user物件;這是因為雖然處於託管狀態,但是這個物件是存在屬性值的物件,並沒有把他刪除,只是隔絕了它與資料庫的打交道的通道。

System.out.println(user.getName());

}

//測試load()方法;不執行sql語句,用到的時候才執行

@Test

publicvoid testLoad(){


Session session = HibernateUtil.getSession();

User user = (User) session.load(User.class,1);

//這裡輸出id也不會執行sql語句,直接從上面你傳進去的id中獲取id,沒有從資料庫中查詢,所以也不執行sql語句

System.out.println(user.getId());

//而輸出name就不一樣了,這時其實就是例項化了代理物件,這是的代理物件有了name的屬性,這時即使你關閉了session,也可以通過這個物件獲取到name;如果註釋這句,即不例項化代理物件,又在關閉session後執行輸出name屬性,這時會報錯:could not initialize proxy

// System.out.println(user.getName());

HibernateUtil.close();

System.out.println(user.getName());

}


}

情況二:set集合上取消懶載入

//測試如果在對映檔案中將集合屬性中的懶載入設定為false後將連帶著orders表中資料一塊提出來,即兩條select語句

@Test

publicvoid find(){


Session session = HibernateUtil.getSession();

Customer cus = (Customer) session.get(Customer.class,3);

System.out.println(cus.getCname());


//用到下面這種方法輸出會出現兩天sql語句,而且是分開的;如果用到懶載入會出現先輸出兩條sql語句,在輸出結果

//這裡不能直接方法鏈式輸出cus.getOrd().getOname();因為cus.getOrd()返回的是一個set集合

Set<Orders> orders = cus.getOrd();

System.err.println(orders.size());


HibernateUtil.close(); 

}


方法三:<one-to-one>,<many-to-one> 取消懶載入

@Test

publicvoid find(){

//預設使用懶載入,即用著一條sql語句就輸出一條;如果設定延時載入為false後輸出兩條sql語句,將不需要的顧客資訊也查出來

Session session = HibernateUtil.getSession();

Orders ord = (Orders) session.get(Orders.class,3);

System.out.println(ord.getOname());


HibernateUtil.close();

}