一、get不支援lazy,load支援lazy。
二、採用get載入數據,如果沒有符合的數據,回傳null,而load則拋出異常。
三、執行load()時,先從Session查找當前的物件是否存在,如果不存在,則從資料庫中去查詢,如果不存在這條記錄,則扔異常;執行get()的時候,不論Session中是否存在目前對象,直接從資料庫去查詢,如果不存在,則傳回null。
四、load()方法可以傳回實體的代理類別實例,而get()永遠只回傳實體類別.
get的例子:
1public void testGetMethod() {
2 Session session = null;
3 try {
4 session = HibernateUtils.getSession();
5 session.beginTransaction();
6
7 //馬上發出查詢sql,載入User物件
8 User user = (User)session.get(User.class, "40280d01b9bf210011b9bf2c2ff0002");1b9bf210011b9bf2c2ff0002"); user.getName());
10
11 user.setName("張三");
12 美 〠); }catch(Exception e) {
14 e.printStackTrace();
15 session.getTransaction() .rollback();
16 }finally {
17 HibernateUtils.closeSession(session);〠418『18『4);在執行第8行語句的時候,如果資料庫有相關的記錄,則立刻發出sql語句,即使是沒有第9行中user.getName()方法調用,如果沒有匹配的記錄則返回來一個null。
load方法例子:
1public void testLoadMethod() {
2 Session session = null;
3 try {
4 session = HibernateUtils.getSession();
5 session.beginTransaction();
6
7 //不會發出查詢sql,因為load方法實作了lazy(懶載入或延遲載入)
8 //延遲載入:只有真正使用這個物件的時候,才載入(發出sql語句)
9 代理方式
10 User user = (User)session.load(User.class, "402880d01b96210011b9bf2b2ff0002"); Name());
12 user.setName ("李四");
13 session.getTransaction().commit();
14 }catch(Exception e) {Tef🀀 }catch(Exception e) {Tef🀵㜀 㜀〦. session.getTransaction().rollback();
17 }finally {
18 HibernateUtils.closeSession(session);
19 }
20 句} ,延遲載入只有真正使用該物件的時候才進行加載, 發出sql語句,關鍵是11句程式碼。如果load方法裡面的id在資料庫表裡面沒有相關的,則會發出ObjectNotFoundException 異常。
PS:可以透過設定斷點,用debug來追蹤相關變數跟物件的改變,這樣就可以很清楚地理解load和get這兩個方法。
hibernate,session load出來的物件是
持久態:物件於資料庫記錄建立對應關係並保持同步。物件被綁定在持久化上下文當中,今後它的任何狀態變化、資料變更均處於工作單元的管理之下,這就是持久態。 session.load在hibernate3.2提供的預設延遲載入方式,我覺得load出來的是一個代理,也可以說是持久態(自己這麼理解的,如有誤請個高手糾正)。
了解延遲加載,對理解延遲加載有所幫助:
延遲載入機制是為了避免一些無謂的效能開銷而提出來的,所謂延遲載入就是當在真正需要資料的時候,才真正執行資料載入操作。在Hibernate中提供了對實體物件的延遲載入以及對集合的延遲加載,另外在Hibernate3中也提供了屬性的延遲載入。下面我們就分別介紹這些種類的延遲載入的細節。
A、實體物件的延遲加載:
如果想對實體物件使用延遲加載,必須要在實體的映射設定檔中進行相應的配置,如下所示:
…
透過將class的lazy屬性設為true,來開啟實體的延遲載入特性。如果我們執行下面的程式碼:
User user=(User)session.load(User.class,”1”);(1)
System.out.println(user.getName());(2)
當運行到(1)處時,Hibernate並沒有發起對資料的查詢,如果我們此時透過一些調試工具(比如JBuilder2005的Debug工具),觀察此時user物件的記憶體快照,我們會驚奇的發現,此時回傳的可能是User$EnhancerByCGLIB$$bede8986類型的對象,而且其屬性為null,這是怎麼回事?還記得前面我曾講過session.load()方法,會回傳實體物件的代理類別對象,這裡所回傳的物件類型就是User對象的代理類別物件。在Hibernate中透過使用CGLIB,來實現動態建構一個目標對象的代理類對象,並且在代理類對像中包含目標對象的所有屬性和方法,而且所有屬性均被賦值為null。透過偵錯器顯示的記憶體快照,我們可以看出此時真正的User對象,是包含在代理對象的CGLIB$CALBACK_0.target屬性中,當程式碼運行到(2)處時,此時呼叫user.getName( )方法,這時透過CGLIB賦予的回呼機制,實際上呼叫CGLIB$CALBACK_0.getName()方法,當呼叫該方法時,Hibernate會先檢查CGLIB$CALBACK_0.target屬性是否為null,如果不為空,則呼叫目標物件的getName方法,如果為空,則會發起資料庫查詢,產生類似這樣的SQL語句:select * from user where id='1';來查詢數據,並建構目標對象,並且將它賦值到CGLIB $CALBACK_0.target屬性中。
這樣,透過一個中間代理對象,Hibernate實現了實體的延遲加載,只有當用戶真正發起獲得實體對象屬性的動作時,才真正會發起資料庫查詢操作。所以實體的延遲載入是用透過中間代理類別完成的,所以只有session.load()方法才會利用實體延遲加載,因為只有session.load()方法才會傳回實體類別的代理類別物件。
B、 集合類型的延遲載入:
B、 集合類型的延遲載入:
在Hibernate的延遲載入機制中,針對集合類型的應用,意義是最為重大的,因為這有可能使效能得到大幅度的提高,為此Hibernate進行了大量的努力,其中包括對JDK Collection的獨立實現,我們在一對多關聯中,定義的用來容納關聯對象的Set集合,並不是java.util.Set類型或其子類型,而是net.sf. hibernate.collection.Set類型,透過使用自訂集合類別的實現,Hibernate實現了集合類型的延遲載入。為了對集合類型使用延遲加載,我們必須如下配置我們的實體類別的關於關聯的部分:
…..
透過將
User user=(User)session.load(User.class,”1”);
Collection addset=user.getAddresses(); (1)
Iterator it=addset. ); (2)
while(it.hasNext()){
🎜Address address=(Address)it.next();
System.out.println(address.getAddress());
}
當程式執行到(1)時,這時並不會啟動關聯資料的查詢來載入關聯數據,只有在執行到(2)時,真正的資料讀取操作才會開始,這時Hibernate會根據快取中符合條件的資料索引,來尋找符合條件的實體物件。
這裡我們引入了一個全新的概念-資料索引,以下我們將接一下什麼是資料索引。在Hibernate中對集合類型進行快取時,是分兩部分進行緩存的,首先緩存集合中所有實體的id列表,然後緩存實體對象,這些實體對象的id列表,就是所謂的資料索引。當查找資料索引時,如果沒有找到對應的資料索引,這時就會一條select SQL的執行,獲得符合條件的數據,並建構實體物件集合和資料索引,然後傳回實體物件的集合,並且將實體物件和資料索引納入Hibernate的快取之中。另一方面,如果找到對應的資料索引,則從資料索引中取出id列表,然後根據id在快取中查找對應的實體,如果找到就從快取中返回,如果沒有找到,在發起select SQL查詢。在這裡我們看出了另外一個問題,這個問題可能會對效能產生影響,這就是集合類型的快取策略。如果我們如下配置集合類型:
…..
>
這裡我們應用了
User user=(User)session.load(User.class,”1”);
Collection addset=user.getAddresses();
Iterator it=addset.iterator() ;
while(it.hasNext()){
Address address=(Address)it.next();
System.out.println(address.getAddress());
}
System.out. println(“Second query…”);
User user2=(User)session.load(User.class,”1”);
Collection it2=user2.getAddresses();
while(it2.Next( )){
Address address2=(Address)it2.next();
System.out.println(address2.getAddress());
}
運行這段代碼,會得到類似下面的輸出:
Select * from user where id='1';
Select * from address where user_id='1';
Tianjin
Dalian
Second quejin
Dalian
.Second queryquery;
Select * from address where id='2';
Tianjin
Dalian
我們看到,當第二次執行查詢時,執行了兩個對address表的查詢操作,為什麼會這樣?這是因為當第一次載入實體後,根據集合類型緩存策略的配置,只對集合資料索引進行了緩存,而並沒有對集合中的實體物件進行緩存,所以在第二次再次載入實體時, Hibernate找到了對應實體的資料索引,但是根據資料索引,卻無法在快取中找到對應的實體,所以Hibernate根據找到的資料索引發起了兩條select SQL的查詢操作,這裡造成了對效能的浪費,怎樣才能避免這種情況呢?我們必須對集合類型中的實體也指定快取策略,所以我們要如下對集合類型進行設定:
…..
>
此時Hibernate會對集合類型中的實體也進行緩存,如果根據這個配置再次運行上面的程式碼,將會得到類似如下的輸出:
Select * from user where id='1';
Select * from address where user_id='1';
Tianjin
Dalian
Second query……
Dalian
這時將不再有根據資料索引進行查詢的SQL語句,因為此時可以直接從快取中取得集合類型中存放的實體物件。
C、 屬性延遲載入:
在Hibernate3中,引入了一種新的功能-屬性的延遲加載,這個機制又為取得高效能查詢提供了有力的工具。在前面我們講大數據對象讀取時,在User對像中有一個resume字段,該字段是一個java.sql.Clob類型,包含了用戶的簡歷信息,當我們加載該對象時,我們不得不每一次都要載入這個字段,而不論我們是否真的需要它,而且這種大數據物件的讀取本身會帶來很大的效能開銷。在Hibernate2中,我們只有透過我們前面講過的面性能的粒度細分,來分解User類,來解決這個問題(請參照那一節的論述),但是在Hibernate3中,我們可以透過屬性延遲載入機制,來使我們獲得只有當我們真正需要操作這個字段時,才去讀取這個字段數據的能力,為此我們必須如下配置我們的實體類:
……
透過對
String sql=”from User user where user.name='zx' ”;
Query query=session.createQuery(sql); (1)
List list=query .list();
for(int i=0;i
User user=(User)list.get(i);
System.out.println(user. getName());
System.out.println(user.getResume()); (2)
}
當執行到(1)時,會產生類似如下的SQL語句:
當執行到(1)處時,會產生類似如下的SQL語句:
Select id, age,name from user where name='zx';
這時Hibernate會檢索User實體中所有非延遲載入屬性對應的欄位數據,當執行到(2)處時,會產生類似如下的SQL語句:
Select resume from user where id='1';
Employee employee2 = (Employee) session.load(Employee.class, new Integer(100));
會拋出net.sf.hibernate.ObjectNotFoundException。
如果不想拋出該Exception, 應該使用session.get(Class clazz, Serializable id);
2. load一條資料庫存在的記錄
1)如果session的Cache中存在,那麼直接傳回該引用。
Session session = HibernateSessionFactory.currentSession();
Transaction tx = session.beginTransaction();
Employee employee1 = new Employee();
employee1.setAge((irth)10ate); ;
employee1.setEmployeeName("HairRoot");
employee1.setGender('m');
employee1.setMarried(false);
session.save(employee1);
System. /employ1 已經儲存
Employee employee2 = (Employee) session.load(Employee.class, new Integer(/*employee1.getId()*/100));
System.out.println(employee2); ();
session.close();
可以發現employee1和employee2是同一個引用。開啟show_sql=true也會發現,hibernate並沒有傳送一條select語句給資料庫。
2)如果session的Cache中不存在,那麼會立即發送一條select。
Session session = HibernateSessionFactory.currentSession();
Transaction tx = session.beginTransaction();
Employee employee = (Employee) session.load(Employee.class, new Integer005); );
tx.commit();
session.close();
這時會啟動對讀取欄位資料真正的操作。