Hibernate的延遲加載

hibernate中,延遲加載大致可以分爲兩類,一類是延遲屬性加載,另一類是延遲關聯實體加載。

普通屬性:分兩種情況,一種是集合屬性,一種是非集合屬性(如String、Integer......)

集合屬性的延遲加載通過PersistentSet、 PersistentList、PersistentBag、PersistentMap、PersistentSortedMap、PersistentSortedSet作爲代理類來實現,代理類中保存了session以及owner屬性,owner屬性表示了集合屬性所屬的one側的實體。

非集合類屬性的延遲加載相對比較複雜。僅通過@Basic(fetch = FetchType.LAZY)註解是無法實現延遲加載的。需要讓實體實現FieldHandled接口,聲明FieldHandler屬性,通過攔截器原理注入對應的FieldHandler屬性,起到類似於上述代理類的作用,FieldHandler同樣也保持了session,以及需要延遲加載的屬性。

@Basic(fetch = FetchType.LAZY)
@Column(name="CONTENT")
public String getContent() {
    if (fieldHandler != null) {
        return (byte[]) fieldHandler.readObject(this, "content", content);
    }
    return null;
}
public void setContent(byte[] content) {
    this.content = content;
}
@Override
public void setFieldHandler(FieldHandler handler) {
    this.fieldHandler = handler;
}
@Override
public FieldHandler getFieldHandler() {
    return this.fieldHandler;
}
Hibernate官網對非結合屬性的延遲加載有如下的評論:

Lazy property loading requires buildtime bytecode instrumentation. If your persistent classes are not enhanced, Hibernate will ignore lazy property settings and return to immediate fetching.

A different way of avoiding unnecessary column reads, at least for read-only transactions, is to use the projection features of HQL or Criteria queries. This avoids the need for buildtime bytecode processing and is certainly a preferred solution.

大致的意思就是:應該是因爲,我們並未用到編譯時字節碼增強技術的原因。如果只對部分property進行延遲加載的話,hibernate還提供了另外的方式,也是更爲推薦的方式,即HQL或者條件查詢。

更爲推薦的方式說白了就是在查詢的時候就過濾掉不需要的屬性,以下列出HQL和Criterial的兩種實現方法:

// criterial實現
criteria.setProjection(
                Projections.projectionList().add(Projections.property("id"), "id")
                        .add(Projections.property("age"), "age")).setResultTransformer(
                Transformers.aliasToBean(User.class));

// HQL實現
Query query = session.createQuery("select u.id as id,u.age as age from User u where u.id=2");
query.setResultTransformer(Transformers.aliasToBean(User.class));


關聯實體:分兩種情況,一種是多對一,另一種是一對一。

關聯實體是多個實體時(包括一對多、多對多):此時關聯實體將以集合的形式存在,Hibernate 將使用 PersistentSet、PersistentList、PersistentMap、PersistentSortedMap、PersistentSortedSet 等集合來管理延遲加載的實體。這就是前面所介紹的情形。

關聯實體是單個實體時(包括一對一、多對一):當 Hibernate 加載某個實體時,延遲的關聯實體將是一個動態生成代理對象。Hibernate 使用 Javassist 項目動態生成的代理類——當 Hibernate 延遲加載關聯實體時,將會採用 Javassist 生成一個動態代理對象,這個代理對象將負責代理“暫未加載”的關聯實體。但是在一對一關係中,延遲加載是有陷阱的。

一對一關聯一般有兩種形式,一種是主鍵關聯;另一種是外鍵關聯。

主鍵關聯:

Husband:id,name
Wife:id,name

Husband實體:
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
@PrimaryKeyJoinColumn
getWife()
@GenericGenerator(name = "Wife", strategy = "foreign", parameters = { @Parameter(name = "property", value = "husband") })
Wife實體:
@OneToOne(mappedBy = "wife", fetch = FetchType.LAZY)
getHusband()
以上是hibernate中的配置。其中“optional=false”的配置時關鍵,否則即使配置了fetch策略爲lazy,也無法做到在獲取husband實體的時候延遲加載wife實體。optional的默認值是true,表示關聯的實體可以爲null。在一對一的延遲加載中,hibernate並非一定對需要延遲加載的實體生成一個動態代理對象,而是當被關聯的實體確定不爲null時,纔會生成,否則直接將其置爲null。所以問題就來了,對於兩個通過主鍵關聯的一對一實體,在獲取到其中一個實體後,要判斷與之關聯的實體是否存在,則必須要再查詢一次數據庫纔可以。這也就是爲什麼在設置了延遲加載策略後,hibernate還是立即發送了一次查詢請求給數據庫。

要解決一對一關係中的延遲加載,共有兩種方法:一種就是上面提到的,把optional設置爲false,即關聯的實體一定不爲null。這樣一來,hibernate就會立即爲配置延遲加載的實體生成一個動態代理類。

第二種方法就是通過外鍵來關聯,即:

Husband:id,name, wife_id
Wife:id,name
其中husband_id與Husbande中的id關聯

Husband實體:
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "wife_id", , referencedColumnName = "id")
getWife()
Wife實體:
@OneToOne(mappedBy = "wife", fetch = FetchType.LAZY)
getHusband()
這樣一來,直接可以通過husband中的wife_id的null與否來判斷wife實體是否爲null。


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