1. 程式人生 > >Hibernate的查詢優化、抓取策略

Hibernate的查詢優化、抓取策略

敘:在hibernate中存在類級別查詢和關聯級別查詢兩種,前一個是對一對多關係情況下使用的,後一個是對多對一關係情況下使用的,詳細的請看下面的筆記;


Hibernate查詢優化、抓取策略

延遲載入(lazy載入)先獲取到的是索要查詢的資料的代理物件,當真正使用到該物件中的資料的時候,才會傳送SQL語句,這種機制是Hibernate框架提升效能的方式之一

類級別查詢

類級別查詢是指只查詢一個類的資料,並不牽涉關聯類的資料查詢,類級別查詢只有load()、get()兩個查詢方法,get()查詢是即查即得型別的,load()查詢是延遲載入(懶載入)型別的,執行到查詢語句時並不會立即查詢,而是執行到使用這個查詢語句查詢的結果才會執行查詢語句,get()方法沒有任何查詢策略,load()方法需要策略進行配置,此配置只需要在此類的對映檔案中的class標籤中設定一個lazy屬性,這個屬性只有false、true(預設);
以下是get()、load()查詢的使用;

@Test
	public void demo1(){
		Session session = HibernateUtils.openSession();
		Transaction bt = session.beginTransaction();
		
		//Customer cust = session.get(Customer.class, 2l);
		Customer cust = session.load(Customer.class, 2l);
		
		System.out.println(cust);
		
		bt.commit();
	}

兩個方法,在使用方面並沒有什麼區別,而且如果直接執行demo的話並不會看的出來結果,要想看到結果需要在get()/load()行與列印輸出那行各打一個斷點,然後使用debug來進行檢視;
會發現,程式碼執行到load()方法那一行時並不會有查詢語句出現,當執行到列印那一行時也不會有任何反應,但是一旦資料有事務提交後就會使用關聯的session進行資料庫查詢,載入資料,所查詢的東西完全打印出來;但是如果是get()方法的demo的話,當執行到列印那一行的時候就會出現查詢語句進行查詢,當有事務提交時就會打印出來查詢的結果;
在hibernate中有一個查詢策略的,就是關於類級別的查詢查略,同樣在實體類的對映檔案中進行配置,配置如下程式碼所示:

<hibernate-mapping package="com.java.domain" >
	<class name="Customer" table="cst_customer" lazy="true" >

在對映檔案的中的class標籤中,使用lazy屬性,這個翻譯過來就是懶,對應的是load()方法的別稱–懶載入;其屬性值只有false、true(預設值)這兩個,true就是允許延遲載入(懶載入),false就是不使用懶載入,在使用false屬性值配置的情況下使用load()方法是不會起到延遲載入的效果的,這時的load()方法和get()方法沒有任何差別;

實驗方法:
把lazy屬性設定成false,然後debug執行一遍load()方法的demo,檢視執行到列印、事務提交這兩個點的程式碼輸出情況,與不編寫lazy屬性時(其實lazy屬性對get()方法沒有任何影響,這樣做是為了避嫌)get()方法的debug結果;

注意:
上面提到,load()方法懶載入是使用關聯的session進行資料庫查詢並載入資料的,也就是說當關聯的session關閉後查詢到的代理物件也不存在了,查詢也不存在了,程式碼如下所示:

@Test
	/*
	 * 錯誤程式碼演示:關聯的session被銷燬,查詢終結
	 */
	public void demo2(){
		Session session = HibernateUtils.openSession();
		Transaction bt = session.beginTransaction();
		
		Customer cust = session.load(Customer.class, 2l);
		bt.commit();
		session.close();
		System.out.println(cust);
	}

session被銷燬後這個session中所夾帶的或者說所代表的資料已將被銷燬,當再次呼叫這個session所關聯的資料時會報如下圖所示的錯:
報錯資訊

結論:
為了提高效率,儘量使用load()方法;


關聯級別查詢

注意:關聯級別查詢在學習時比較拗腦^_^沒錯,就是拗腦,表述不清楚的地方還望海涵,多讀幾遍會清晰很多

關聯級別查詢是指,所要查詢資料中包含這個類A的資料以及與這類A相關聯類B的資料,即,查詢的資料包含關聯類資料;
關聯級別查詢分為集合查詢和關聯屬性查詢兩種,集合查詢就是以查詢類為主,在查詢類的檔案中進行設定相應的配置,關聯屬性查詢是指在關聯類中設定相應的配置;
在類級別查詢中我們瞭解了lazy屬性,因為類級別查詢不涉及到多個類的關聯查詢,因此不需要考慮集合查詢的策略(抓取策略),而關聯級別查詢則需對查詢的類載入問題進行配置;

集合查詢策略
集合查詢即是指一對多關係的情況下使用集合的查詢策略,所使用的屬性及其屬性值、配置位置、程式碼展示以及總結:
屬性名:屬性值
Lazy屬性:

屬性值------------ 備註
true 延遲載入,懶載入(預設)
false 立即載入
extra 極其懶惰,查詢的結果和懶載入效果類似,區別:如果只獲得集合的size值,只需要查詢集合的size(count語句),使用這個配置是比較方便的,但一般情況下最好還是不用此配置;

fetch屬性:(抓取策略屬性)

屬性值------------ 備註
select 單表查詢載入(預設)
join 使用多表查詢載入集合
subselect 使用子查詢載入集合:根據類A的多個Customer物件查詢與之對應關聯的所有LinkMan物件的資訊;只有查詢多個Customer物件的關聯物件資料時才會使用subselect配置,當只查詢一個Customer物件的關聯物件資料時其效果和select的配置效果一樣;

這些屬性的配置位置:在類的對映檔案中set標籤中進行設定;
配置程式碼展示:

/*
*一般的抓取快取策略demo:
*/
@Test
	public void demo1(){
		Session session = HibernateUtils.openSession();
		Transaction bt = session.beginTransaction();
		
		Customer cust = session.get(Customer.class, 2l);		
		System.out.println(cust);
		
		bt.commit();
	}

對應的對映檔案配置:
對映檔案配置

子查詢(subselect查詢集合方式)demo程式碼和一般的不太一樣,因此拎出來單獨記錄;

/*
*子查詢的Demo
*/
    @Test
    	/*
    	 * 關聯查詢:
    	 * fetch:subselect
    	 * lazy:true
    	 */
    	public void demo3(){
    		Session session = HibernateUtils.openSession();
    		Transaction bt = session.beginTransaction();
    		
    		String hql = "from Customer";
    		Query custs = session.createQuery(hql);
    		List<Customer> list = custs.list();
    		for (Customer customer : list) {
    			System.out.println(customer.getLinkMens());
    			System.out.println(customer.getLinkMens().size());
    		}
    		bt.commit();
    }

對映檔案配置:
子查詢檔案配置

總結/注意:

  1. fetch的作用:控制抓取關聯物件的時候傳送的SQL語句的格式;
  2. lazy的作用:控制查詢其關聯物件的時候是否採用延遲載入;
  3. 當fetch設定成多表查詢時(join),lazy不論設定什麼值都是無效的;

關聯屬性查詢策略

關聯查詢即是指多對一關係的類的對映檔案進行配置,所使用的屬性及其屬性值、配置位置、程式碼展示以及總結:
Lazy屬性:

屬性值 備註
proxy 由關聯他的類A的類級別載入策略決定;
false 立即載入

fetch屬性:

屬性值 備註
select 單表查詢載入
join 使用多表查詢載入集合

屬性配置位置:在類的對映檔案中的標籤中進行設定
Demo:

@Test
	public void demo4(){
		Session session = HibernateUtils.openSession();
		Transaction bt = session.beginTransaction();
		
		LinkMan linkMan = session.get(LinkMan.class, 2l);
		Customer customer = linkMan.getCustomer();
		System.out.println(customer);
		
		bt.commit();
}

對映檔案配置展示:
關聯屬性查詢策略
注意:

  • 需要注意當使用懶載入的proxy屬性值時,意為由關聯這個B物件的A物件的類級別載入策略決定,注意,是A物件的類級別載入策略,就是寫在class標籤中進行配置的;

優化查詢總結:

為了提高效率,fetch一般使用預設值select,lazy使用預設值true,即,全部使用預設值即可;


No-Session問題解決辦法:

No-session 問題導致的原因是用到的資料物件所關聯的session物件已經被清除,session物件所關聯的資料需要在web、service、dao層中進行傳遞,最終查詢的資料結果需要放到頁面中展示給使用者,因此要擴大session的作用範圍,所使用的技術就是過濾器(filter),具體解決思路如下:
no-session問題解決辦法思路圖
既是,在經過過濾器時就會建立一個session,使用這個session直到放行完成所有操作後在進行清理session並提交事務;


pass:學完了查詢優化和抓取策略基本上hibernate的入門什麼的已經算是完成了,但是對於hibernate框架來說這只是皮毛,真正的學習是往後的研究探索,希望大家能有所建樹對於此框架的學習~一起加油吧^_^
# 《本章完》