Hibernate中,延遲加載針對屬性類別能夠分爲兩類,一類是延遲屬性加載,另外一類是延遲關聯實體加載。數據庫
屬性有能夠分爲兩種類型:一種是集合屬性,一種是非集合屬性(如String、Integer……)。session
集合屬性的延遲加載經過PersistentSet、 PersistentList、PersistentBag、PersistentMap、PersistentSortedMap、PersistentSortedSet做爲代理類來實現,代理類中保存了session以及owner屬性,owner屬性表示了集合屬性所屬的one側的實體。app
非集合屬性的延遲加載相對比較複雜。僅經過@Basic(fetch = FetchType.LAZY)註解是沒法實現延遲加載的。須要讓實體實現FieldHandled接口,聲明FieldHandler屬性,經過攔截器原理注入對應的FieldHandler屬性,起到相似於上述代理類的做用,FieldHandler一樣也保持了session,以及須要延遲加載的屬性。下面的代碼實現了非集合屬性的延遲加載ide
@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官網對非結合屬性的延遲加載有以下的評論:fetch
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.ui
大體的意思就是:應該是由於,咱們並未用到編譯時字節碼加強技術的緣由。若是隻對部分property進行延遲加載的話,hibernate還提供了另外的方式,也是更爲推薦的方式,即HQL或者條件查詢。
更爲推薦的方式說白了就是在查詢的時候就過濾掉不須要的屬性,如下列出HQL和Criterial的兩種實現方法:this
// 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
關聯實體是多個實體時(包括一對多、多對多):此時關聯實體將以集合的形式存在,Hibernate 將使用 PersistentSet、PersistentList、PersistentMap、PersistentSortedMap、PersistentSortedSet 等集合來管理延遲加載的實體。這就是前面所介紹的情形。代理
關聯實體是單個實體時(包括一對1、多對一):當 Hibernate 加載某個實體時,延遲的關聯實體將是一個動態生成代理對象。Hibernate 使用 Javassist 項目動態生成的代理類——當 Hibernate 延遲加載關聯實體時,將會採用 Javassist 生成一個動態代理對象,這個代理對象將負責代理「暫未加載」的關聯實體。可是在一對一關係中,延遲加載是有陷阱的。一對一關聯通常有兩種形式,一種是主鍵關聯;另外一種是外鍵關聯。code
數據表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的時候,其主鍵爲null(還未生成),wife的主鍵也爲null,此時save的話,hibernate理論上應該先insert husband實體,而後用生成出來的husband_id做爲wife的主鍵進行insert。可事與願違,hibernate認爲此狀況違背了optional=false的假設,故會拋異常IdentifierGenerationException,即wife的id爲null。具體緣由不太知曉,猜想多是由於持久化的順序,先持久化husband,此時wife的主鍵爲null,可是你又配置了optional=false,故先後矛盾而拋出異常。
數據表Husband,3列屬性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。