hibernate延遲加載

轉自[url=http://hi.baidu.com/javasea/item/515e82d8f7f7d8f792a97433]JAVA-太陽-海[/url]
Hibernate對象關係映射提供延遲的與非延遲的對象初始化。非延遲加載在讀取一個對象的時候會將與這個對象所有相關的其他對象一起讀取出來。這有時會導致成百的(如果不是成千的話)select語句在讀取對象的時候執行。這個問題有時出現在使用雙向關係的時候,經  
Hibernate 對象關係映射提供延遲的與非延遲的對象初始化。非延遲加載在讀取一個對象的時候會將與這個對象所有相關的其他對象一起讀取出來。這有時會導致成百的(如果 不是成千的話)select語句在讀取對象的時候執行。這個問題有時出現在使用雙向關係的時候,經常會導致整個數據庫都在初始化的階段被讀出來了。當然, 你可以不厭其煩地檢查每一個對象與其他對象的關係,並把那些最昂貴的刪除,但是到最後,我們可能會因此失去了本想在ORM工具中獲得的便利。一個明顯的解 決方法是使用Hibernate提供的延遲加載機制。這種初始化策略只在一個對象調用它的一對多或多對多關係時纔將關係對象讀取出來。這個過程對開發者來 說是透明的,而且只進行了很少的數據庫操作請求,因此會得到比較明顯的性能提升。這項技術的一個缺陷是延遲加載技術要求一個Hibernate會話要在對 象使用的時候一直開着。這會成爲通過使用DAO模式將持久層抽象出來時的一個主要問題。爲了將持久化機制完全地抽象出來,所有的數據庫邏輯,包括打開或關 閉會話,都不能在應用層出現。最常見的是,一些實現了簡單接口的DAO實現類將數據庫邏輯完全封裝起來了。一種快速但是笨拙的解決方法是放棄DAO模式, 將數據庫連接邏輯加到應用層中來。這可能對一些小的應用程序有效,但是在大的系統中,這是一個嚴重的設計缺陷,妨礙了系統的可擴展性。
在Web層進行延遲加載幸運的是,Spring框架爲Hibernate延遲加載與DAO模式的整合提供了一種方便的解決方法。對那些不熟悉 Spring與Hibernate集成使用的人,我不會在這裏討論過多的細節,但是我建議你去了解Hibernate 與Spring集成的數據訪問。以一個Web應用爲例,Spring提供了OpenSessionInViewFilter和 OpenSessionInViewInterceptor。我們可以隨意選擇一個類來實現相同的功能。兩種方法唯一的不同就在於 interceptor 在Spring容器中運行並被配置在web應用的上下文中,而Filter在Spring之前運行並被配置在web.xml中。不管用哪個,他們都在請求 將當前會話與當前(數據庫)線程綁定時打開Hibernate會話。一旦已綁定到線程,這個打開了的Hibernate會話可以在DAO實現類中透明地使 用。這個會話會爲延遲加載數據庫中值對象的視圖保持打開狀態。一旦這個邏輯視圖完成了,Hibernate會話會在Filter的doFilter方法或 者Interceptor的postHandle方法中被關閉。下面是每個組件的配置示例: Interceptor的配置: Filter的配置 hibernateFilter org.springframework.orm.hibernate.support.OpenSessionInViewFilter hibernateFilter *. spring 實現Hibernate的Dao接口來使用打開的會話是很容易的。事實上,如果你已經使用了Spring框架來實現你的Hibernate Dao,很可能你不需要改變任何東西。方便的HibernateTemplate公用組件使訪問數據庫變成小菜一碟,而DAO接口只有通過這個組件纔可以 訪問到數據庫。
下面是一個示例的DAO:
public class HibernateProductDAO extends HibernateDaoSupport implements ProductDAO { 
public Product getProduct(Integer productId) {
return (Product)getHibernateTemplate().load(Product.class, productId);
}
public Integer saveProduct(Product product) {
return (Integer) getHibernateTemplate().save(product);
}
public void updateProduct(Product product)
{
getHibernateTemplate().update(product);
}
}

在業務邏輯層中使用延遲加載即使在視圖外面,Spring框架也通過使用AOP 攔截器 HibernateInterceptor來使得延遲加載變得很容易實現。這個Hibernate 攔截器透明地將調用配置在Spring應用程序上下文中的業務對象中方法的請求攔截下來,在調用方法之前打開一個Hibernate會話,然後在方法執行 完之後將會話關閉。讓我們來看一個簡單的例子,假設我們有一個接口BussinessObject:
public interface BusinessObject { public void doSomethingThatInvolvesDaos(); } 

類BusinessObjectImpl實現了BusinessObject接口:
public class BusinessObjectImpl implements BusinessObject { 
public void doSomethingThatInvolvesDaos() { // lots of logic that calls // DAO classes Which access // data objects lazily
} }

通過在Spring應用程序上下文中的一些配置,我們可以讓將調用BusinessObject的方法攔截下來,再令它的方法支持延遲加載。看看下面的一 個程序片段: com.acompany.BusinessObject hibernateInterceptor 當businessObject被調用的時候,HibernateInterceptor打開一個Hibernate會話,並將調用請求傳遞給 BusinessObjectImpl對象。當 BusinessObjectImpl執行完成後,HibernateInterceptor透明地關閉了會話。應用層的代碼不用瞭解任何持久層邏輯,還 是實現了延遲加載。在單元測試中測試延遲加載最後,我們需要用J-Unit來測試我們的延遲加載程序。我們可以輕易地通過重寫TestCase類中的 setUp和tearDown方法來實現這個要求。我比較喜歡用這個方便的抽象類作爲我所有測試類的基類。

public abstract class MyLazyTestCase extends TestCase {
private SessionFactory sessionFactory; private Session session;
public void setUp() throws Exception {
super.setUp();
SessionFactory sessionFactory = (SessionFactory) getBean("sessionFactory");
session = SessionFactoryUtils.getSession(sessionFactory, true);
Session s = sessionFactory.openSession();
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(s));
}
protected Object getBean(String beanName)
{ //Code to get objects from Spring application context
}
public void tearDown() throws Exception
{ super.tearDown(); SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
Session s = holder.getSession(); s.flush(); TransactionSynchronizationManager.unbindResource(sessionFactory);
SessionFactoryUtils.closeSessionIfNecessary(s, sessionFactory);
}
}

我們首先說利用lazy=false來解決問題,這種方法是很好用,但是在實際的過程並不實用,如果你某個對象關聯好幾個甚至十幾個自對象,那麼每次加載 一個的話要執行很多HQL語句,可想而知這個效率問題啦,特別是在使用列表的時候。所以不實用,而在上述所說的“在業務邏輯層中使用延遲加載” 也是這種情況吧。很多使用WEB的朋友都喜歡利用openSessionView來加載這個問題。不錯我也喜歡用,簡單方便。但是說幾種情況: 1.在quartz中總不能使用openSessionView模式吧 2.在spring後攔截的過程中,如果取攔截對象中的子對象利用openSessionView也不行
解決方案:在one的一方的set方法里加入屬性:lazy="false";
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章