Hibernate性能調優--關聯實體的延遲加載


默認情況下,Hibernate也會採用延遲加載來加載關聯實體,不管是一對多關聯、還是一對一關聯、多對多關聯,Hibernate 默認都會採用延遲加載。

對於關聯實體,可以將其分爲兩種情況:

  • 關聯實體是多個實體時(包括一對多、多對多):此時關聯實體將以集合的形式存在,Hibernate 將使用 PersistentSetPersistentListPersistentMapPersistentSortedMapPersistentSortedSet 等集合來管理延遲加載的實體。這就是前面所介紹的情形。
  • 關聯實體是單個實體時(包括一對一、多對一):當 Hibernate 加載某個實體時,延遲的關聯實體將是一個動態生成代理對象。

當關聯實體是單個實體時,也就是使用<many-to-one.../> 或 <one-to-one.../> 映射關聯實體的情形,這兩個元素也可通過 lazy屬性來指定延遲加載。

下面例子把Address 類也映射成持久化類,此時 Address 類也變成實體類,Person 實體與 Address實體形成一對多的雙向關聯。此時的映射文件代碼如下:

Person.hbm.xml

 

<hibernate-mappingpackage="org.crazyit.app.domain">

<classname="Person" table="person_inf">

<idname="id" column="person_id">

<generatorclass="identity" />

</id>

<propertyname="name" type="string" />

<propertyname="age" type="int" />

<!-- 映射集合屬性,集合元素是其他持久化實體 沒有指定 cascade 屬性,指定不控制關聯關係 -->

<setname="addresses" inverse="true">

<!-- 指定關聯的外鍵列-->

<keycolumn="person_id" />

<!-- 用以映射到關聯類屬性 -->

<one-to-manyclass="Address" />

</set>

</class>

<!-- 映射Address 持久化類 -->

<classname="Address" table="address_inf">

<!-- 映射標識屬性addressId -->

<idname="addressId" column="address_id">

<!-- 指定主鍵生成器策略 -->

<generatorclass="identity" />

</id>

<!-- 映射普通屬性detail -->

<propertyname="detail" />

<!-- 映射普通屬性zip -->

<propertyname="zip" />

<!-- 必須指定列名爲person_id, 與關聯實體中 key 元素的column 屬性值相同 -->

<many-to-onename="person" class="Person" column="person_id"

not-null="true"/>

</class>

</hibernate-mapping>

接下來程序通過如下代碼片段來加載ID 爲 1 的 Person 實體:

 // 打開上下文相關的 Session
 Session session =
sessionFactory.getCurrentSession();
 Transaction tx =session.beginTransaction();
 Address address = (Address)session.get(Address.class , 1); //<1>
 System.out.println(address.getDetail());

爲了看到 Hibernate 加載 Address 實體時對其關聯實體的處理,我們在 <1>號代碼處設置一個斷點,在 Eclipse 中進行 Debug,此時可以看到 Eclipse Console 窗口輸出如下 SQL 語句:

    select
        address0_.address_id asaddress1_1_0_,
        address0_.detail as detail1_0_,
        address0_.zip as zip1_0_,
        address0_.person_id asperson4_1_0_
    from
        address_inf address0_
    where
        address0_.address_id=?

從這條SQL 語句不難看出,Hibernate 加載 Address 實體對應的數據表抓取記錄,並未從 Person實體對應的數據表中抓取記錄,這是延遲加載發揮了作用。

從Eclipse 的 Variables 窗口看到如圖所示的輸出:

延遲加載的實體

從圖可以清楚地看到,此時Address 實體所關聯的 Person 實體並不是 Person 對象,而是一個 Person_$$_javassist_0 類的實例,這個類是Hibernate 使用 Javassist 項目動態生成的代理類——當 Hibernate 延遲加載關聯實體時,將會採用 Javassist生成一個動態代理對象,這個代理對象將負責代理“暫未加載”的關聯實體。

只要應用程序需要使用“暫未加載”的關聯實體,Person_$$_javassist_0代理對象會負責去加載真正的關聯實體,並返回實際的關聯實體——這就是最典型的代理模式。

單擊圖所示Variables 窗口中的 person 屬性(也就是在調試模式下強行使用 person 屬性),此時看到 Eclipse 的 Console窗口輸出如下的 SQL 語句:

    select
        person0_.person_id asperson1_0_0_,
        person0_.name as name0_0_,
        person0_.age as age0_0_
    from
        person_inf person0_
    where
        person0_.person_id=?

上面SQL 語句就是去抓取“延遲加載”的關聯實體的語句。此時可以看到 Variables 窗口輸出圖所示的結果:

已加載的實體

Hibernate採用“延遲加載”管理關聯實體的模式,其實就在加載主實體時,並未真正去抓取關聯實體對應數據,而只是動態地生成一個對象作爲關聯實體的代理。當應用程序真正需要使用關聯實體時,代理對象會負責從底層數據庫抓取記錄,並初始化真正的關聯實體。

在Hibernate 的延遲加載中,客戶端程序開始獲取的只是一個動態生成的代理對象,而真正的實體則委託給代理對象來管理——這就是典型的代理模式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章