懶加載可以提高性能嗎?
不可以簡單的說"能",因為Hibernate的關系映射拖累了SQL的性能,所以想出懶加載來彌補.只是彌補而以,不會超越.所以大家不要想着使用了懶加載總體性能就提高了,其實總體性能不下降就萬幸了.
懶加載為Hibernate中比較常用的特性之一,下面我們詳細來了解下懶加載的原理和注意事項
Load()方法的懶加載原理
在Hibernate中,查詢方法有兩個,分別是get()和load(),這兩種方法的不同就是load()擁有懶加載的特性。Load()方法就是在查詢某一條數據的時候並不會直接將這條數據以指定對象的形式來返回,而是在你真正需要使用該對象里面的一些屬性的時候才會去數據庫訪問並得到數據。他的好處就是可以減少程序本身因為與數據庫頻繁的交互造成的處理速度緩慢。
以一個Person類做例子,我們寫一個查詢的方法如下:
1 public static void query(int id){ 2 Session session=null; 3 try{ 4 session=HibernateUtil.getSession(); 5 Person person=(Person) session.load(Person.class, id); 6 //System.out.println(person.getName()); 7 }catch(HibernateExceptionex){ 8 ex.printStackTrace(); 9 }finally{ 10 if(session!=null){ 11 session.close(); 12 } 13 } 14 }
然后在Hibernate配置文件中添加以下屬性
<property name="hibernate.show_sql">true</property>
這條屬性的作用為將Hibernate運行時產生的每一條SQL語句都打印出來
運行上述方法后,我們並沒有看到Hibernate打印任何查詢語句,這時我們可以將注釋的語句重新調回來,讓他打印查詢到的person的name。這時我們可以看到Hibernate產生的查詢語句並看到person的name屬性。這就是懶加載了。
那么在Hibernate懶加載的時候,返回的對象是空的嗎?答案是否定的,我們可以通過打印person.getClass()方法來驗證,打印出來的結果並不是null,而是一個Person后面加了一堆很奇怪的字符的類。可以肯定的是懶加載的對象並不是空,而且這個對象的類型不是Person類。那么他到底是什么呢?
其實這個兌現我們管它叫做代理對象,而這個對象所屬的類是Person類的子類,是Hibernate自動實現的一個子類。這個子類的特點是:當你訪問person對象的某一個屬性的時候,他會自動查詢數據庫中對應這個對象的數據並返回,這就是為什么在創建對象關系映射的時候要求實體類不能夠為final類型的原因了。
但是這個對象是有一個生命周期的,我們可以改寫上述方法如下:
1 public static Person query(int id){ 2 Session session=null; 3 Person person=null; 4 try{ 5 session=HibernateUtil.getSession(); 6 person=(Person)session.load(Person.class, id); 7 //System.out.println(person.getName()); 8 }catch(HibernateExceptionex){ 9 ex.printStackTrace(); 10 }finally{ 11 if(session!=null){ 12 session.close(); 13 } 14 } 15 return person; 16 }
調用這個方法並返回查詢到的代理對象,我們可以在返回該對象后打印該對象的name屬性,這時會拋出一個org.hibernate.LazyInitializationException異常
這就是懶加載不能初始化異常,這就說明了一件事,懶加載的時候如果想通過代理對象查詢數據庫,需要在該session關閉以前才可以。但如果一定要在session關閉以后再使用代理對象的話,Hibernate中定義了一個初始化代理對象的方法initialize(),通過該方法即可將代理對象初始化。
注:在使用代理對象的getId()方法和getClass()方法的時候,並不會拋出不能初始化異常,因為這兩個屬性並不用查詢數據庫。
懶加載可以用於關系映射和集合屬性的操作,而且懶加載是可以關閉並打開的,下面我們會根據不同的情況分析一下懶加載。
一對一的懶加載分析
一對一的懶加載並不常用,因為懶加載的目的是為了減少與數據庫的交互,從而提高執行效率,而在一對一關系中,主表中的每一條數據只對應從表的一條數據庫,就算都查詢也不會增加多少交互的成本,而且主表不能有contrained=true,所以主表是不能懶加載的。(從表可以有)
注:當fetch設置為join時,懶加載就會失效。因為fetch的作用是抓取方式,他有兩個值分別問select和join,默認值為select。即在設為join時,他會直接將從表信息以join方式查詢到而不是再次使用select查詢,這樣導致了懶加載的失效。
一對多和多對多的懶加載分析
與一對一關聯不同,一對多和一對多的關聯下,主表的每一條屬性都會對應從表的多條數據,這個時候懶加載就顯得非常有效了。比如一個部門里面有多個員工,如果沒有懶加載,每查詢這個部門的時候都會查詢出多個員工,這會大大增加與數據庫交互的成本。所以Hbernate默認的是加入懶加載的。這就是查詢集合屬性的時候返回的是一個PersistentIndexed*類型對象的原因。該對象其實就是一個代理對象。當然,可以在映射文件中通過將lazy屬性設為假來禁用。
多對一的懶加載分析
雖然多對一與一對一關系方式相同,但是在Hibernate中多對一時,默認是進行懶加載的。另外有一點需要注意的是懶加載並不會區分集合屬性里面是否有值,即使是沒有值,他依然會使用懶加載,這也是懶加載不夠完善的地方之一。
懶加載的一些細節擴充
有的時候,我們在session關閉后仍需要使用懶加載的代理對象來查詢數據庫,這時我們就需要將代理對象初始化,不過問題是,當我們不能確定初始化后就一定使用該對象的時候怎么辦,這樣不是又白白浪費了資源嗎?我們可以在使用代理對象的方法里面加入一個布爾值參數,這樣當我們不需要初始化代理對象的時候只要將布爾參數設為假。但也是有缺點的,因為在調用的時候,尤其是在別人使用的時候,參數越多,方法學習成本就會增加,所以請酌情處理。
懶加載也可以用於某些簡單屬性,但是因為實現起來比較復雜,而且效果並不明顯,所以並不推薦。