1. 程式人生 > >Hibernate之載入策略(延遲載入與即時載入)和抓取策略(fetch)

Hibernate之載入策略(延遲載入與即時載入)和抓取策略(fetch)

假設現在有Book和Category兩張表,表的關係為雙向的一對多,表結構如下:

假設現在我想查詢id為2的那本書的書名,使用session.get(...)方法:

 Session session=HibernateUtil.getSession();
 Book book =(Book) session.get(Book.class,2);
 System.out.println(book.getName());

當執行完第二行程式碼,還未執行第三行時,控制檯已經打印出了sql語句,執行第三行時打印出書名"鬥破蒼穹".

而如果使用session.load(..)查詢時:

 Session session=HibernateUtil.getSession();
 Book book =(Book) session.load(Book.class,1);
 System.out.println(book.getName());

當執行完第二行程式碼還未執行第三行時,控制檯什麼都沒有列印,執行第三行時,控制檯打印出sql語句和書名"鬥破蒼穹".

1.看出get和load的區別了嗎?

實際上,當使用get方法查詢時,程式立即去訪問資料庫(實際上是先去一級快取session中查詢,沒有發現的話再去二級快取,再沒有的話才去訪問資料庫),得到id=2的Book,並且打印出sql語句,而是用load方法查詢時,load並未立即去訪問資料庫,他先是返回了一個Book的代理物件,當你真正要用到Book中資訊時,才去訪問資料庫.load支援延遲載入,get不支援延遲載入,當然如果設定了lazy=false,get和load都會直接去訪問資料庫,都變成即時載入.

2.get/load方法還有一個很重要的區別就是:

load方式檢索不到的話會丟擲org.hibernate.ObjectNotFoundException異常

get方法檢索不到的話會返回null

這就引出了即時載入和延時載入的概念,通俗的說,即時載入,就是立即去資料庫查詢,延遲載入,就是真正需要的時候才去資料庫查詢,這類似於單例模式中的懶漢式和餓漢式的載入方式.

假設我現在想通過查詢Book,來得到Book所對應的Category,如果設定為即時載入,當載入Book時,會自動載入Category,如果設定為延遲載入,則載入Book時,不會載入Category,只有當第一次呼叫getCategory(),時,才去執行sql語句,載入Category.

  一般來說,延遲載入要比即時載入節省資源,但是如果處理不當,延遲載入容易丟擲延遲載入異常(LazyInitializationException).這是因為延遲載入時,只有第一次呼叫getCategory()時才會載入Category資料,如果這時候資料庫連線已經關閉了,就會因為無法載入資料而丟擲異常.

  在*.hbm.xml中可以設定載入方式,class標籤中可以設定:lazy="true",開啟延遲載入,預設就是lazy="true".

  在set/bag標籤下,預設也是lazy="true",支援延遲載入,也叫懶載入. <many-to-many> or<one -o-many>

  單端關聯(many_to_one或者one_to_one)上也可以設定lazy="true".預設也是true支援懶載入.

下面是網路上一段關於get和load方法的詳細異同,寫的不錯,貼在這裡:

一、get和load方法都是根據id去獲得對應資料的,但是獲得機制不同:如果使用get方法,hibernate會去確認該id對應的資料是否存在,它首先會去session中去查詢(session快取其實就hibernate的一級快取),如果沒有,再去二級快取中去查詢,如果再沒有,就去資料庫中查詢,仍然沒有找到的話,就返回null

  而使用load方法的話,hibernate會認定該id對應的資料一定存在,它也會先去session快取中去查詢,如果沒有找到,hibernate會根據lazy屬性值來確定是否使用延遲載入。如果lazy=‘true’ ,就使用延遲載入,返回該代理物件,等到真正訪問到該物件的屬性時才會去二級快取中查詢,如果沒有,再去資料庫中查詢,如果還沒有,就丟擲org.hibernate.ObjectNotFoundException異常。如果lazy='false' 則不使用延遲載入,這是load的訪問機制就和get一樣了。

  二、對於load和get方法返回型別:雖然好多書中都這麼說:“get()永遠只返回實體類”,但實際上這是不正確的,get方法如果在 session快取中找到了該id對應的物件,如果剛好該物件前面是被代理過的,如被load方法使用過,或者被其他關聯物件延遲載入過,那麼返回的還是 原先的代理物件,而不是實體類物件,如果該代理物件還沒有載入實體資料(就是id以外的其他屬性資料),那麼它會查詢二級快取或者資料庫來載入資料,但是 返回的還是代理物件,只不過已經載入了實體資料。

抓取策略:

  在hibernate的官方文件中對於抓取策略,是這麼定義的:當應用程式需要在(hibernate實體物件圖的)關聯關係間進行物件導航的時候,hibernate如何獲取關聯物件的策略.

fetch="select":當查詢關聯物件通過select語句去查詢,Select語句的發出時機,是根據lazy的值來確定的,如果lazy="false",那麼在獲取物件時,就會發出一條select語句,將關聯物件查詢出來,就是說,我們在查詢Book資訊的時候會自動把Category的資料也查詢出來,但如果lazy="true",那麼只有在獲取關聯物件的時候才會發出select語句去查詢.

fetch="join":當查詢Book資訊時,會通過outer join把關聯的物件Category一起查詢出來,這個時候lazy無效,所有資料會立即查詢出來.

fetch="subselect":如果要查詢關聯集合的內容,會查詢之前已經查詢出來的所有關聯集合的內容,<category對應了多張Book,如果查詢了"玄幻類","武俠類",那麼在使用"玄幻類"和"武俠類"對應的集合物件("所對應的書籍資訊"),會將他們的書籍資訊一併查詢出來,