Hibernate的延迟加载
----------------------------------------------? 一 -------------------------------------------
轉(zhuǎn)自:http://blog.csdn.net/java958199586/article/details/7069901
hibernate延遲加載(懶加載)詳解
一.什么是懶加載?他的作用?
延遲加載,也叫懶加載,它是Hibernate為提高程序執(zhí)行效率而提供的一種機制,即只有真正使用該對象的數(shù)據(jù)時才會創(chuàng)建。
Hibernate中主要是通過代理(proxy)機制來實現(xiàn)延遲加載。它的具體過程:Hibernate叢數(shù)據(jù)庫獲取某一個對象數(shù)據(jù)時、獲取某一個對象的集合屬性值時,或獲取某一個對象所關(guān)聯(lián)的另一個對象時,由于沒有使用該對象的數(shù)據(jù),hibernate并不是數(shù)據(jù)庫加載真正的數(shù)據(jù),而只是為該對象創(chuàng)建一個代理對象來代表這個對象,這個對象上的所有屬性都是默認(rèn)值;只有在真正需要使用該對象的數(shù)據(jù)時才創(chuàng)建這個真實對象,真正從數(shù)據(jù)庫中加載它的數(shù)據(jù),這樣在某些情況下,就可以提高查詢效率。
有如下程序代碼:
User user=(User)session.load(clazz, id);//直接返回的是代理對象
System.out.println(user.getId());//沒有發(fā)送sql語句到數(shù)據(jù)庫加載
user.getName();//創(chuàng)建真實的User實例,并發(fā)送sql語句到數(shù)據(jù)庫中
注意:1.不能判斷User=null;代理對象不可能為空
代理對象的限制:和代理關(guān)聯(lián)的session對象,如果session關(guān)閉后訪問代理則拋異常。session關(guān)閉之前訪問數(shù)據(jù)庫
2.getId()方法不行因為參數(shù)為ID,getClass()方法不用訪問數(shù)據(jù)庫就可以得到的數(shù)據(jù)
Hibernate中默認(rèn)采用延遲加載的情況主要有以下幾種
1,當(dāng)調(diào)用session上的load()加載一個實體時,會采用延遲加載。
2,當(dāng)session加載某個實體時,會對這個實體中的集合屬性值采用延遲加載
3當(dāng)session加載某個實體時,會對這個實體所有單端關(guān)聯(lián)的另一個實體對象采用延遲加載。
二.關(guān)閉延遲加載
?????? 延遲加載確實會給程序的查詢效率帶來好處,但有時明確知道數(shù)據(jù)需要立即加載,如果Hibernate先默認(rèn)使用延遲加載,而后又必須去數(shù)據(jù)庫加載,反而會降低效率
1.?????加載單個實體,如果不需要延遲加載,就可以使用session的get()方法。
2.?????當(dāng)session加載某個實體時,不需要對這個實體中的集合屬性值延遲加載,而是要立即加載。這是可以在映射文件中這個集合的配置元素(set bag list)添加屬性lazy=false;
3.?????當(dāng)session加載某個實體時,不需要對這個實體所單端關(guān)聯(lián)的另一個實體對象延遲加載,就可以在影射文件中針對這個單端關(guān)聯(lián)的配置元素(<one-to-one><many-to-one>)添加lazy=false;
三.抓取策略
?? 通過asm和cglib二個包實現(xiàn);Domain是非final的。
1.session.load懶加載。
2.one-to-one(元素)懶加載:
?????? 必需同時滿足下面三個條件時才能實現(xiàn)懶加載
?????? (主表不能有constrained=true,所以主表沒有懶加載)
?????? lazy!=false 2)constrained=true3)fetch=select
3.one-to-many (元素)懶加載:1)lazy!=false 2)fetch=select
4.many-to-one (元素) :1)lazy!=false 2)fetch=select
5.many-to-many (元素) :1)lazy!=false 2)fetch=select
6.能夠懶加載的對象都是被改寫過的代理對象,當(dāng)相關(guān)聯(lián)的session沒有關(guān)閉時,訪問這些懶加載對象(代理對象)的屬性(getId和 getClass除外)hibernate會初始化這些代理,或用Hibernate.initialize(proxy)來初始化代理對象;當(dāng)相關(guān)聯(lián)的 session關(guān)閉后,再訪問懶加載的對象將出現(xiàn)異常。
---------------------------------------------------- 二 -------------------------------------------------
PS:Hibernate 的延遲加載(lazy load)本質(zhì)上就是代理模式的應(yīng)用,我們在過去的歲月里就經(jīng)常通過代理模式來降低系統(tǒng)的內(nèi)存開銷、提升應(yīng)用的運行性能。Hibernate 充分利用了代理模式的這種優(yōu)勢,并結(jié)合了 Javassist 或 CGLIB 來動態(tài)地生成代理對象,這更加增加了代理模式的靈活性,Hibernate 給這種用法一個新名稱:延遲加載。無論怎樣,充分分析、了解這些開源框架的實現(xiàn)可以更好的感受經(jīng)典設(shè)計模式的優(yōu)勢所在
http://blog.csdn.net/xc635960736/article/details/7049863
PS:
如果是在one的一方查詢many,在one的一方的set方法里加入屬性:lazy="false";
如果是在many的一方查詢one,在many的一方的many-to-one里加上lazy="false";
--------------------------------------------- 三 ----------------------------------------------
以下轉(zhuǎn)自: http://developer.51cto.com/art/200907/133249.htm
Hibernate延時加載包括延遲初始化錯誤,這是運用Hibernate開發(fā)項目時最常見的錯誤。如果對一個類或者集合配置了延遲檢索策略,那么必須當(dāng)代理類實例或代理集合處于持久化狀態(tài)(即處于Session范圍內(nèi))時,才能初始化它。如果在游離狀態(tài)時才初始化它,就會產(chǎn)生延遲初始化錯誤。
下面把Customer.hbm.xml文件的< class>元素的lazy屬性設(shè)為true,表示使用延遲檢索策略:
當(dāng)執(zhí)行Session的load()方法時,Hibernate不會立即執(zhí)行查詢CUSTOMERS表的select語句,僅僅返回Customer類的代理類的實例,這個代理類具由以下特征:
(1)由Hibernate在運行時動態(tài)生成,它擴展了Customer類,因此它繼承了Customer類的所有屬性和方法,但它的實現(xiàn)對于應(yīng)用程序是透明的。
(2)當(dāng)Hibernate創(chuàng)建Customer代理類實例時,僅僅初始化了它的OID屬性,其他屬性都為null,因此這個代理類實例占用的內(nèi)存很少。
(3)當(dāng)應(yīng)用程序第一次訪問Customer代理類實例時(例如調(diào)用customer.getXXX()或customer.setXXX ()方法), Hibernate會初始化代理類實例,在初始化過程中執(zhí)行select語句,真正從數(shù)據(jù)庫中加載Customer對象的所有數(shù)據(jù)。但有個例外,那就是當(dāng)應(yīng)用程序訪問Customer代理類實例的getId()方法時,Hibernate不會初始化代理類實例,因為在創(chuàng)建代理類實例時OID就存在了,不必到數(shù)據(jù)庫中去查詢。
提示:Hibernate采用CGLIB工具來生成持久化類的代理類。CGLIB是一個功能強大的Java字節(jié)碼生成工具,它能夠在程序運行時動態(tài)生成擴展 Java類或者實現(xiàn)Java接口的代理類。
以下代碼先通過Session的load()方法加載Customer對象,然后訪問它的name屬性:
在運行session.load ()方法時Hibernate不執(zhí)行任何select語句,僅僅返回Customer類的代理類的實例,它的OID為1,這是由load()方法的第二個參數(shù)指定的。當(dāng)應(yīng)用程序調(diào)用customer.getName()方法時,Hibernate會初始化Customer代理類實例,從數(shù)據(jù)庫中加載 Customer對象的數(shù)據(jù),執(zhí)行以下select語句:
當(dāng)< class>元素的lazy屬性為true,會影響Session的load()方法的各種運行時行為,下面舉例說明。
1.如果加載的Customer對象在數(shù)據(jù)庫中不存在,Session的load()方法不會拋出異常,只有當(dāng)運行customer.getName()方法時才會拋出以下異常:
2.如果在整個Session范圍內(nèi),應(yīng)用程序沒有訪問過Customer對象,那么Customer代理類的實例一直不會被初始化,Hibernate不會執(zhí)行任何select語句。以下代碼試圖在關(guān)閉Session后訪問Customer游離對象:
由于引用變量customer引用的Customer代理類的實例在Session范圍內(nèi)始終沒有被初始化,因此在執(zhí)行customer.getName()方法時,Hibernate會拋出以下異常(Hibernate延時加載的問題之一):
由此可見,Customer代理類的實例只有在當(dāng)前Session范圍內(nèi)才能被初始化。
3.net.sf.hibernate.Hibernate類的initialize()靜態(tài)方法用于在Session范圍內(nèi)顯式初始化代理類實例,isInitialized()方法用于判斷代理類實例是否已經(jīng)被初始化。例如:
以上代碼在Session范圍內(nèi)通過Hibernate類的initialize()方法顯式初始化了Customer代理類實例,因此當(dāng)Session關(guān)閉后,可以正常訪問Customer游離對象。
4.當(dāng)應(yīng)用程序訪問代理類實例的getId()方法時,不會觸發(fā)Hibernate初始化代理類實例的行為,例如:
當(dāng)應(yīng)用程序訪問customer.getId()方法時,該方法直接返回Customer代理類實例的OID值,無需查詢數(shù)據(jù)庫。由于引用變量 customer始終引用的是沒有被初始化的Customer代理類實例,因此當(dāng)Session關(guān)閉后再執(zhí)行customer.getName()方法, Hibernate會拋出以下異常(Hibernate延時加載的問題之一):
解決方法:
由于hibernate采用了lazy=true,這樣當(dāng)你用hibernate查詢時,返回實際為利用cglib增強的代理類,但其并沒有實際填充;當(dāng)你在前端,利用它來取值(getXXX)時,這時Hibernate才會到數(shù)據(jù)庫執(zhí)行查詢,并填充對象,但此時如果和這個代理類相關(guān)的 session已關(guān)閉掉,就會產(chǎn)生種錯誤.
在做一對多時,有時會出現(xiàn)"could not initialize proxy - clothe owning Session was sed,這個好像是hibernate的緩存問題.問題解決:需要在< many-to-one>里設(shè)置lazy="false". 但有可能會引發(fā)另一個異常叫
解決方法:在web.xml中加入?
就可以了。
------------------------------------------------- 四? -------------------------------------------------
轉(zhuǎn)自:http://www.blogjava.net/sutao/articles/158759.html
在Web層進(jìn)行延遲加載
幸運的是,Spring框架為Hibernate延遲加載與DAO模式的整合提供了一種方便的解決方法。對那些不熟悉Spring與 Hibernate集成使用的人,我不會在這里討論過多的細(xì)節(jié),但是我建議你去了解Hibernate與Spring集成的數(shù)據(jù)訪問。以一個Web應(yīng)用為例,Spring提供了OpenSessionInViewFilter和OpenSessionInViewInterceptor。我們可以隨意選擇一個類來實現(xiàn)相同的功能。兩種方法唯一的不同就在于interceptor在Spring容器中運行并被配置在web應(yīng)用的上下文中,而Filter在 Spring之前運行并被配置在web.xml中。不管用哪個,他們都在請求將當(dāng)前會話與當(dāng)前(數(shù)據(jù)庫)線程綁定時打開Hibernate會話。一旦已綁定到線程,這個打開了的Hibernate會話可以在DAO實現(xiàn)類中透明地使用。這個會話會為延遲加載數(shù)據(jù)庫中值對象的視圖保持打開狀態(tài)。一旦這個邏輯視圖完成了,Hibernate會話會在Filter的doFilter方法或者Interceptor的postHandle方法中被關(guān)閉。下面是每個組件的配置示例:
Interceptor的配置:
<beans>
<bean id="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="openSessionInViewInterceptor"/>
</list>
</property>
<property name="mappings">
</bean>
<bean name="openSessionInViewInterceptor"
class="org.springframework.orm.hibernate.support.OpenSessionInViewInterceptor">
<property name="sessionFactory"><ref bean="sessionFactory"/></property>
</bean>
</beans>
Filter的配置
<web-app>
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>
org.springframework.orm.hibernate.support.OpenSessionInViewFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>*. spring </url-pattern>
</filter-mapping>
</web-app>
實現(xiàn)Hibernate的Dao接口來使用打開的會話是很容易的。事實上,如果你已經(jīng)使用了Spring框架來實現(xiàn)你的Hibernate Dao,很可能你不需要改變?nèi)魏螙|西。方便的HibernateTemplate公用組件使訪問數(shù)據(jù)庫變成小菜一碟,而DAO接口只有通過這個組件才可以訪問到數(shù)據(jù)庫。下面是一個示例的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);
}
}
在業(yè)務(wù)邏輯層中使用延遲加載
即使在視圖外面,Spring框架也通過使用AOP 攔截器 HibernateInterceptor來使得延遲加載變得很容易實現(xiàn)。這個Hibernate 攔截器透明地將調(diào)用配置在Spring應(yīng)用程序上下文中的業(yè)務(wù)對象中方法的請求攔截下來,在調(diào)用方法之前打開一個Hibernate會話,然后在方法執(zhí)行完之后將會話關(guān)閉。讓我們來看一個簡單的例子,假設(shè)我們有一個接口BussinessObject:
public???? interface??? BusinessObject???? {
public???? void??? doSomethingThatInvolvesDaos();
}
類BusinessObjectImpl實現(xiàn)了BusinessObject接口:
public???? class??? BusinessObjectImpl??? implements??? BusinessObject???? {
public???? void??? doSomethingThatInvolvesDaos()???? {
//??? lots of logic that calls
//??? DAO classes Which access
//??? data objects lazily?
}?
}?
通過在Spring應(yīng)用程序上下文中的一些配置,我們可以讓將調(diào)用BusinessObject的方法攔截下來,再令它的方法支持延遲加載。看看下面的一個程序片段:
<beans>
<bean id="hibernateInterceptor" class="org.springframework.orm.hibernate.HibernateInterceptor">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<bean id="businessObjectTarget" class="com.acompany.BusinessObjectImpl">
<property name="someDAO"><ref bean="someDAO"/></property>
</bean>
<bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target"><ref bean="businessObjectTarget"/></property>
<property name="proxyInterfaces">
<value>com.acompany.BusinessObject</value>
</property>
<property name="interceptorNames">
<list>
<value>hibernateInterceptor</value>
</list>
</property>
</bean>
</beans>
當(dāng)businessObject被調(diào)用的時候,HibernateInterceptor打開一個Hibernate會話,并將調(diào)用請求傳遞給 BusinessObjectImpl對象。當(dāng)BusinessObjectImpl執(zhí)行完成后,HibernateInterceptor透明地關(guān)閉了會話。應(yīng)用層的代碼不用了解任何持久層邏輯,還是實現(xiàn)了延遲加載。
在單元測試中測試延遲加載
最后,我們需要用J-Unit來測試我們的延遲加載程序。我們可以輕易地通過重寫TestCase類中的setUp和tearDown方法來實現(xiàn)這個要求。我比較喜歡用這個方便的抽象類作為我所有測試類的基類。
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);
}
}
總結(jié)
以上是生活随笔為你收集整理的Hibernate的延迟加载的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 03_MyBatis基本查询,mappe
- 下一篇: MyBatis延迟加载及在spring中