session中的load與get

來源:互聯網
上載者:User
一、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, "402880d01b9bf210011b9bf2c2ff0002");
9      System.out.println("user.name=" + user.getName());
10      
11      user.setName("張三");
12      session.getTransaction().commit();
13    }catch(Exception e) {
14      e.printStackTrace();
15      session.getTransaction().rollback();
16    }finally {
17      HibernateUtils.closeSession(session);
18    }
19  }

PS:在執行第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      //hibernate消極式載入實現原理是代理方式
10      User user = (User)session.load(User.class, "402880d01b9bf210011b9bf2b2ff0002");
11      System.out.println("user.name=" + user.getName());
12      user.setName("李四");
13      session.getTransaction().commit();
14    }catch(Exception e) {
15      e.printStackTrace();
16      session.getTransaction().rollback();
17    }finally {
18      HibernateUtils.closeSession(session);
19    }
20  }

PS:在執行到10句代碼的時候並沒有立刻發出sql語句,由於load實現了lazy消極式載入,消極式載入只有真正使用該對象的時候才進行載入, 發出sql語句,關鍵是11句代碼。如果load方法裡面的id在資料庫表裡面沒有相關的,則會發出ObjectNotFoundException 異常。

PS:可以通過設定斷點,用debug來跟蹤相關變數跟對象的改變,這樣就可以很清晰理解load和get這兩個方法。

hibernate,session load出來的對象是

持久態:對象於資料庫記錄建立對應關係並保持同步。對象被綁定在持久化上下文當中,今後它的任何狀態變化、資料變更均處於工作單元的管理之下,這就是持久態。session.load在hibernate3.2中提供的預設消極式載入方式,我覺得load出來的是一個代理,也可以說是持久態(自己這麼理解的,如有誤請個高手糾正)。





瞭解消極式載入,對理解消極式載入有所協助:

消極式載入機制是為了避免一些無謂的效能開銷而提出來的,所謂消極式載入就是當在真正需要資料的時候,才真正執行資料載入操作。在Hibernate中提供了對實體物件的消極式載入以及對集合的消極式載入,另外在Hibernate3中還提供了對屬性的消極式載入。下面我們就分別介紹這些種類的消極式載入的細節。

A、實體物件的消極式載入:

如果想對實體物件使用消極式載入,必須要在實體的映射設定檔中進行相應的配置,如下所示:

<hibernate-mapping>

<class name=”com.neusoft.entity.User” table=”user” lazy=”true”>

……

</class>

</hibernate-mapping>

通過將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、 集合類型的消極式載入:

在Hibernate的消極式載入機制中,針對集合類型的應用,意義是最為重大的,因為這有可能使效能得到大幅度的提高,為此Hibernate進行了大量的努力,其中包括對JDK Collection的獨立實現,我們在一對多關聯中,定義的用來容納關聯對象的Set集合,並不是java.util.Set類型或其子類型,而是net.sf.hibernate.collection.Set類型,通過使用自訂集合類的實現,Hibernate實現了集合類型的消極式載入。為了對集合類型使用消極式載入,我們必須如下配置我們的實體類的關於關聯的部分:

<hibernate-mapping>

<class name=”com.neusoft.entity.User” table=”user”>

…..

<set name=”addresses” table=”address” lazy=”true” inverse=”true”>

<key column=”user_id”/>

<one-to-many class=”com.neusoft.entity.Arrderss”/>

</set>

</class>

</hibernate-mapping>

通過將<set>元素的lazy屬性設定為true來開啟集合類型的消極式載入特性。我們看下面的代碼:

User user=(User)session.load(User.class,”1”);

Collection addset=user.getAddresses(); (1)

Iterator it=addset.iterator(); (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查詢。在這裡我們看出了另外一個問題,這個問題可能會對效能產生影響,這就是集合類型的緩衝策略。如果我們如下配置集合類型:

<hibernate-mapping>

<class name=”com.neusoft.entity.User” table=”user”>

…..

<set name=”addresses” table=”address” lazy=”true” inverse=”true”>

<cache usage=”read-only”/>

<key column=”user_id”/>

<one-to-many class=”com.neusoft.entity.Arrderss”/>

</set>

</class>

</hibernate-mapping>

這裡我們應用了<cache usage=”read-only”/>配置,如果採用這種策略來配置集合類型,Hibernate將只會對資料索引進行緩衝,而不會對集合中的實體物件進行緩衝。如上配置我們運行下面的代碼:

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.hasNext()){

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 query……

Select * from address where id=’1’;

Select * from address where id=’2’;

Tianjin

Dalian

我們看到,當第二次執行查詢時,執行了兩條對address表的查詢操作,為什麼會這樣?這是因為當第一次載入實體後,根據集合類型緩衝策略的配置,只對集合資料索引進行了緩衝,而並沒有對集合中的實體物件進行緩衝,所以在第二次再次載入實體時,Hibernate找到了對應實體的資料索引,但是根據資料索引,卻無法在緩衝中找到對應的實體,所以Hibernate根據找到的資料索引發起了兩條select SQL的查詢操作,這裡造成了對效能的浪費,怎樣才能避免這種情況呢?我們必須對集合類型中的實體也指定緩衝策略,所以我們要如下對集合類型進行配置:

<hibernate-mapping>

<class name=”com.neusoft.entity.User” table=”user”>

…..

<set name=”addresses” table=”address” lazy=”true” inverse=”true”>

<cache usage=”read-write”/>

<key column=”user_id”/>

<one-to-many class=”com.neusoft.entity.Arrderss”/>

</set>

</class>

</hibernate-mapping>

此時Hibernate會對集合類型中的實體也進行緩衝,如果根據這個配置再次運行上面的代碼,將會得到類似如下的輸出:

Select * from user where id=’1’;

Select * from address where user_id=’1’;

Tianjin

Dalian

Second query……

Tianjin

Dalian

這時將不會再有根據資料索引進行查詢的SQL語句,因為此時可以直接從緩衝中獲得集合類型中存放的實體物件。

C、 屬性消極式載入:

在Hibernate3中,引入了一種新的特性——屬性的消極式載入,這個機制又為擷取高效能查詢提供了有力的工具。在前面我們講大資料對象讀取時,在User對象中有一個resume欄位,該欄位是一個java.sql.Clob類型,包含了使用者的簡曆資訊,當我們載入該對象時,我們不得不每一次都要載入這個欄位,而不論我們是否真的需要它,而且這種大資料對象的讀取本身會帶來很大的效能開銷。在Hibernate2中,我們只有通過我們前面講過的面效能的粒度細分,來分解User類,來解決這個問題(請參照那一節的論述),但是在Hibernate3中,我們可以通過屬性消極式載入機制,來使我們獲得只有當我們真正需要操作這個欄位時,才去讀取這個欄位資料的能力,為此我們必須如下配置我們的實體類:

<hibernate-mapping>

<class name=”com.neusoft.entity.User” table=”user”>

……

<property name=”resume” type=”java.sql.Clob” column=”resume” lazy=”true”/>

</class>

</hibernate-mapping>

通過對<property>元素的lazy屬性設定true來開啟屬性的消極式載入,在Hibernate3中為了實現屬性的消極式載入,使用了類增強器來對實體類的Class檔案進行強化處理,通過增強器的增強,將CGLIB的回調機制邏輯,加入實體類,這裡我們可以看出屬性的消極式載入,還是通過CGLIB來實現的。CGLIB是Apache的一個開源工程,這個類庫可以操縱java類的位元組碼,根據位元組碼來動態構造符合要求的類對象。根據上面的配置我們運行下面的代碼:

String sql=”from User user where user.name=’zx’ ”;

Query query=session.createQuery(sql); (1)

List list=query.list();

for(int i=0;i<list.size();i++){

User user=(User)list.get(i);

System.out.println(user.getName());

System.out.println(user.getResume()); (2)

}

當執行到(1)處時,會產生類似如下的SQL語句:

Select id,age,name from user where name=’zx’;

這時Hibernate會檢索User實體中所有非消極式載入屬性對應的欄位資料,當執行到(2)處時,會產生類似如下的SQL語句:

Select resume from user where id=’1’;



1。load一條不存在的記錄
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((byte)10);
employee1.setBirthDay(new Date());
employee1.setEmployeeName("HairRoot");
employee1.setGender('m');
employee1.setMarried(false);
session.save(employee1);
System.out.println(employee1);

//employ1 已經被儲存
Employee employee2 = (Employee) session.load(Employee.class, new Integer(/*employee1.getId()*/100));
System.out.println(employee2);
tx.commit();
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 Integer(100));
System.out.println(employee);
tx.commit();
session.close();

後台:Hibernate: select employee0_.id as id0_, employee0_.age as age0_, employee0_.birthDay as birthDay0_, employee0_.employeeName as employee4_0_, employee0_.gender as gender0_, employee0_.married as married0_ from MWS_TEST_EMPLOYEE employee0_ where employee0_.id=?



這時會發起對resume欄位資料真正的讀取操作。

  • 相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.