1. 程式人生 > >hibernate 延遲載入 load和get方法

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