hibernate 延遲載入 load和get方法
延遲載入
延遲載入:
當hibernate從資料庫中載入某個物件時,不載入關聯的物件,而只是生成了代理物件。使用session中的load的方法(在沒有改變lazy屬性,屬性值為true的情況下)獲取到的也是代理物件。
立即載入:
當Hibernate從資料庫中載入某個物件時,載入關聯的物件,生成實際的物件。使用session中的get的方法獲取到的也是實際物件。
延遲載入的好處:
延遲載入策略能避免載入應用程式不需要訪問的關聯物件,以提高應用程式的效能。
立即載入的缺點:
Hibernate在查詢某個物件時,立即查詢與之關聯的物件:
1、當select的語句數目太多,需要頻繁的訪問資料庫,會影響查詢的效能。
2、在應用程式只需要訪問要的物件,而不需要訪問與他關聯的物件的場景下,載入與之關聯的物件完全是多餘的操作,這些多餘的操作是會佔記憶體,這就造成了記憶體空間的浪費。
延遲載入的原理圖:
Hibernate的lazy生效期:
生效期和session一樣的,session關閉,lazy失效。hibernate支援lazy策略只在session開啟狀態下有效。如果session已經關閉了,則會丟擲LazyInitalizationException異常
Hibernate lazy屬性,在3.x後是預設開啟的,在以前版本中預設是關閉的
hibernate在物件關係對映檔案中配置載入策略的方式:(lazy)
1、類級別
元素中lazy屬性的可選值為true(延遲載入)和false(立即載入);
元素中的lazy屬性的預設值為true
2、一對多關聯級別:
元素中的lazy屬性的可選值為:true(延遲載入),extra(增強延遲載入)和false(立即載入);
元素中的lazy屬性的預設值為true
extra其實是一種比較智慧的延遲載入,即呼叫集合的size/contains等方法的時候,hibernate並不會去載入整個集合的資料,而是發出一條聰明的SQL語句,以便獲得需要的值,只有在真正需要用到這些集合元素物件資料的時候,才去發出查詢語句載入所有物件的資料。
3、多對一關聯級別:
元素中lazy屬性的可選值為:proxy(延遲載入),no-proxy(無代理延遲載入)和false(立即載入)
元素中的lazy屬性的預設值為proxy
load和hibernate的主要的區別:
1、load採用的是延遲載入,get 不能採用延遲載入技術而是立刻載入
2、load方法認為資料庫中一定存在要檢索的資料,可以放心的使用代理來延遲載入,如果在使用過程中發現了問題,那麼只能丟擲異常ObjectNotFoundException;而對於get方法,Hibernate一定要獲取到真實的資料才會返回物件,否則返回null。
3、load方法可以返回一個沒有載入實體資料的代理類例項,而get方法永遠返回有實體資料的物件。
(對於load和get方法返回型別:好多書中都說:“get方法永遠只返回實體類”,實際上並不正確,get方法如果在session快取中找到了該id對應的物件,如果剛好該物件前面是被代理過的,如被load方法使用過,或者被其他關聯物件延遲載入過,那麼返回的還是原先的代理物件,而不是實體類物件,如果該代理物件還沒有載入實體資料(就是id以外的其他屬性資料),那麼它會查詢二級快取或者數 據庫來載入資料,但是返回的還是代理物件,只不過已經載入了實體資料。)
下面介紹get和post方法的具體用法及事例。
我的工具類:主要是用來建立session工廠並開啟session的。下面兩個事例中會用到這個類
public class SessionFactoryUtils {
private SessionFactory factory;
private static SessionFactoryUtils factoryUtils;
// 單例模式:把構造方法設定為私有的,說明不可以new這個類的例項。
private SessionFactoryUtils() {
}
// 通過定義這個類的靜態方法,並且返回型別與這個類的型別一樣,來實現對這個類的訪問
public static SessionFactoryUtils getInstance() {
if (factoryUtils == null) {
factoryUtils = new SessionFactoryUtils();
}
return factoryUtils;
}
public SessionFactory openSessionFactory() {
if (factory == null) {
//載入主配置檔案
Configuration configuration = new Configuration().configure();
//建立工廠
factory = configuration.buildSessionFactory();
}
return factory;
}
}
get方法的載入方式
1、因為hibernate規定get方法不使用延遲載入,所以hibernate會去確認該id對應的資料是否存在
2、它會首先在session快取中查詢
3、如果session中找不到,就會去二級快取中查詢
4、如果二級快取中還是沒有找到,就會去資料庫中查詢
5、如果資料庫中還是找不到就返回null。
代理物件實際就是空的物件,並沒有去資料庫中查詢,所以我們叫做代理物件。如果取資料庫查詢了,返回了真實的物件,我們就叫做實體物件。
junit測試類:測試get方法
@org.junit.Test
public void query() {
//呼叫我上面定義的工具類,通過呼叫定義的方法來建立session工廠並開啟session
private SessionFactory sessionFactory = SessionFactoryUtils.getInstance().openSessionFactory();
Session session = sessionFactory.openSession();
Admins adminsGet = session.get(Admins.class, 4);
System.out.println(adminsGet);
}
控制檯輸出:可以發現在直接得到id的時候,它就發起了sql語句去資料庫中查詢了。並沒有延遲載入。
Hibernate: select admins0_.aid as aid1_0_0_, admins0_.adminname as adminnam2_0_0_, admins0_.adminpwd as adminpwd3_0_0_ from admins admins0_ where admins0_.aid=?
4
-------
4,nowyou
Load的載入方式
1、Load採用延遲載入的方式,hibernate的思想是既然這個方法支援延遲載入,它就認為這個物件一定在資料庫中存在,可以放心的使用代理來延遲載入,如果在使用過程中出現了問題就放心的拋異常
2、Load方法會首先查詢session快取,看快取中有沒有這個物件
3、如果快取中沒有這個物件就會去建立個代理物件來管理,因為延遲載入需要代理來執行。但是並沒有去資料庫中查詢
4、只有當你實際使用這個物件的時候,它才會觸發sql語句。這個時候hibernate就會去查詢二級快取和資料庫,如果資料庫中沒有這條語句,就丟擲異常ObjectNotFoundException。
hibernate load方法載入實體物件的時候,會根據對映檔案上 類級別 lazy屬性值的配置,分情況討論:
(1)若為true,即為延遲載入,就是上面的模式
(2)若為false,即為非延遲載入,即立即載入。就跟get方法查詢順序一樣,只是最終若沒發現符合條件的記錄,則會丟擲一個ObjectNotFoundException。
junit測試類:load方法:
@org.junit.Test
public void query() {
//呼叫我上面定義的工具類,通過呼叫定義的方法來建立session工廠並開啟session
private SessionFactory sessionFactory = SessionFactoryUtils.getInstance().openSessionFactory();
Session session = sessionFactory.openSession();
Admins adminsLoad = session.load(Admins.class, 4);
System.out.println(adminsLoad.getAid());
System.out.println("-------");
System.out.println(adminsLoad.getAid()+","+adminsLoad.getAdminname());
}
1、當類級別lazy屬性值為false,即不適用延遲載入的時候:
<hibernate-mapping>
<class name="org.danni.model.entity.Admins" table="admins" lazy="false">
<id name="aid" column="aid">
<generator class="native"></generator>
</id>
<property name="adminname" column="adminname"></property>
<property name="adminpwd" column="adminpwd"></property>
</class>
</hibernate-mapping>
控制檯輸出:可以發現沒有采用延遲載入,當直接輸出id就呼叫了sql去查詢,採用立即查詢的方式
Hibernate: select admins0_.aid as aid1_0_0_, admins0_.adminname as adminnam2_0_0_, admins0_.adminpwd as adminpwd3_0_0_ from admins admins0_ where admins0_.aid=?
4
-------
4,nowyou
2、當lazy屬性為true的時候,即採用延遲載入:
<hibernate-mapping>
<class name="org.danni.model.entity.Admins" table="admins" lazy="true">
<id name="aid" column="aid">
<generator class="native"></generator>
</id>
<property name="adminname" column="adminname"></property>
<property name="adminpwd" column="adminpwd"></property>
</class>
</hibernate-mapping>
控制檯輸出:可以發現,當只是得到id的值的時候並沒有取資料庫中查詢,而是建立了個代理物件來管理。這個代理物件中只儲存了實體物件的id。但是當使用這個物件,獲得這個物件的名字的時候他才觸發了sql語句,它才去二級快取、資料庫中查詢。
4
-------
Hibernate: select admins0_.aid as aid1_0_0_, admins0_.adminname as adminnam2_0_0_, admins0_.adminpwd as adminpwd3_0_0_ from admins admins0_ where admins0_.aid=?
4,nowyou