本文實例講述了Hibernate延遲加載原理與實現方法。分享給大家供大家參考,具體如下:
為了進一步優化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語句中顯示外鍵),否則懶加載不起作用。
2. 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語句@Testpublicvoid 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語句,用到的時候才執行@Testpublicvoid 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語句
@Testpublicvoid 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> 取消懶加載
@Testpublicvoid find(){//默認使用懶加載,即用著一條sql語句就輸出一條;如果設置延時加載為false後輸出兩條sql語句,將不需要的顧客信息也查出來Session session = HibernateUtil.getSession();Orders ord = (Orders) session.get(Orders.class,3);System.out.println(ord.getOname());HibernateUtil.close();}希望本文所述對大家基於Hibernate框架的Java程序設計有所幫助。