懶加載是指程序推遲訪問數據庫,這樣做可以保證有時候不必要的訪問數據庫,因為訪問一次數據庫是比較耗時的。
一、load方法的懶加載
先看如下代碼段
[java]
<span style="font-size:16px;">public class UserManager {
public static void main(String[] args) {
Users user = new Users();
user.setBirthday(new Date());
Name name = new Name();
name.setFirstName("guo");
name.setLastName("zhao");
user.setName(name);
addUser(user);
Users users = getUser(user.getId());
System.out.println(users.getName());
}
static Users getUser(int id){
Session session = HibernateUtil.getSession();
try {
Users users = (Users) session.load(Users.class, id);
return users;
} finally{
session.close();
}
}
static void addUser(Users users){
Session session = null;
Transaction tx = null;
try {
session = HibernateUtil.getSession();
tx = session.beginTransaction();
session.save(users);
tx.commit();
} catch (HibernateException e) {
if (tx!=null) {
tx.rollback();
}
throw e;
}finally{
if(session!=null){
session.close();
}
}
}
}</span>
上面代碼是先增加一個用戶、然後再查詢這個用戶的name組合屬性。運行上面的時候,該程序會拋出這樣一個異常:
Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy – no Session這就是懶加載不能初始化異常。其原因就在於no session。在前面個持久化對象中已經說明:使用load方法時,該方法將具有延遲加載的功能,load方法並不會立即去訪問數據庫,它會返回一個代理對象,隻有當你真正去訪問這個對象的時候,它才會去訪問數據庫。
通過上面的圖,我們看出,hibernate根本就沒有select語句,也就是說hibernate沒有去訪問數據庫,所以這個時候你去訪問它肯定是沒有的。但為什麼沒有拋出空指針異常?沒有拋出空指針異常,也就是說明User對象是存在的,那它是什麼呢?通過輸出user.getClass()可以看出是這樣一個東西:
class com.hibernate.domain.Users_$$_javassist_5。
這個user就是load返回的代理對象。但是這個對象並不是我們所要的。我們所要的是一個User實例對象。
那麼怎麼解決這個問題呢?
第一種方法:在關閉session之前訪問該對象
[java]
<span style="font-size:16px;"> static Users getUser(int id){
Session session = HibernateUtil.getSession();
try {
Users users = (Users) session.load(Users.class, id);
users.getName();
return users;
} finally{
session.close();
}
}</span>
不過這句話看起來會很奇怪。我們通常會采用如下的方式
[java]
<span style="font-size:16px;"> static Users getUser(int id){
Session session = HibernateUtil.getSession();
try {
Users users = (Users) session.load(Users.class, id);
Hibernate.initialize(users);
return users;
} finally{
session.close();
}
}</span>
利用hibernate的initialize()方法將這個代理對象給初始化。
註:在使用代理對象的getId()方法和getClass()方法的時候,並不會拋出不能初始化異常,因為這兩個屬性並不用查詢數據庫。
二、在缺省的情況下,hibernate對於關聯關系會采用懶加載的方式。也就是說1-1、1-N、N-1、N-N都存在懶加載的問題。
2.1、one-to-one懶加載
一對一的懶加載並不常用,因為懶加載的目的是為瞭減少與數據庫的交互,從而提高執行效率,而在一對一關系中,主表中的每一條數據隻對應從表的一條數據庫,就算都查詢也不會增加多少交互的成本,而且主表不能有contrained=true,所以主表是不能懶加載的。但是從表可以有。
實現此種懶加載必須在從對象這邊同時滿足三個條件:
1、lazy!=false(lazy的屬性有三個選項分別為:no-proxy、false和proxy)
2、Constrained = true ;
3、fetch=select。
註:當fetch設置為join時,懶加載就會失效。因為fetch的作用是抓取方式,他有兩個值分別問select和join,默認值為select。即在設為join時,他會直接將從表信息以join方式查詢到而不是再次使用select查詢,這樣導致瞭懶加載的失效。
2.2、one-to-many懶加載
與one-to-one關聯不同,對one-to-many而言,主表的每一條屬性都會對應從表的多條數據,這個時候懶加載就顯得非常有效瞭。比如一個部門裡面有多個員工,如果沒有懶加載,每查詢這個部門的時候都會查詢出多個員工,這會大大增加與數據庫交互的成本。所以Hbernate默認的是加入懶加載的。這就是查詢集合屬性的時候返回的是一個PersistentIndexed*類型對象的原因。該對象其實就是一個代理對象。當然,可以在映射文件中通過將lazy屬性設為假來禁用。
Hibernate默認對one-to-many就是使用的懶加載,但用戶也可以取消懶加載操作:
一:設置lazy=”false”;
二:設置fetch=”join”.
實現此種懶加載必須在從對象這邊同時滿足兩個個條件:
1、lazy!=false(lazy的屬性有三個選項分別為:no-proxy、false和proxy)
2、fetch=select。
2.3、mang-to-one懶加載
此關聯關系的懶加載和one-to-one的懶加載一樣都是可要可不要的,因為對執行效率的提高都不是非常明顯。雖然多對一與一對一關系方式相同,但是在Hibernate中多對一時,默認是進行懶加載的。另外有一點需要註意的是懶加載並不會區分集合屬性裡面是否有值,即使是沒有值,他依然會使用懶加載。
實現此種懶加載必須在從對象這邊同時滿足兩個個條件:
1、lazy!=false(lazy的屬性有三個選項分別為:no-proxy、false和proxy)
2、fetch=select
2.4、many-to-many懶加載
此關聯關系的懶加載和one-to-many的懶加載一樣對程序的執行效率的提高都是非常明顯的。
實現此種懶加載必須在從對象這邊同時滿足兩個個條件:
1、lazy!=false(lazy的屬性有三個選項分別為:no-proxy、false和proxy)
2、fetch=select
能夠懶加載的對象都是被改過的代理對象,當相應的對象沒有關閉時,訪問這些懶加載對象的屬性(getId和getClass除外)hibernate會初始化這些代理,或用hibernate.initalize(proxy)來初始化代理對象;當關閉session後在訪問懶加載的對象就會出現異常
作者:chenssy