Java?hibernate延遲加載get和load的區(qū)別
前言
在hibernate中我們知道如果要從數(shù)據(jù)庫(kù)中得到一個(gè)對(duì)象,通常有兩種方式,一種是通過(guò)session.get()方法,另一種就是通過(guò)session.load()方法,然后其實(shí)這兩種方法在獲得一個(gè)實(shí)體對(duì)象時(shí)是有區(qū)別的,在查詢(xún)性能上兩者是不同的。
一.load加載方式
當(dāng)使用load方法來(lái)得到一個(gè)對(duì)象時(shí),此時(shí)hibernate會(huì)使用延遲加載的機(jī)制來(lái)加載這個(gè)對(duì)象,即:當(dāng)我們使用session.load()方法來(lái)加載一個(gè)對(duì)象時(shí),此時(shí)并不會(huì)發(fā)出sql語(yǔ)句,當(dāng)前得到的這個(gè)對(duì)象其實(shí)是一個(gè)代理對(duì)象,這個(gè)代理對(duì)象只保存了實(shí)體對(duì)象的id值,只有當(dāng)我們要使用這個(gè)對(duì)象,得到其它屬性時(shí),這個(gè)時(shí)候才會(huì)發(fā)出sql語(yǔ)句,從數(shù)據(jù)庫(kù)中去查詢(xún)我們的對(duì)象。
session = HibernateUtil.openSession(); /* * 通過(guò)load的方式加載對(duì)象時(shí),會(huì)使用延遲加載機(jī)制,此時(shí)并不會(huì)發(fā)出sql語(yǔ)句,只有當(dāng)我們需要使用的時(shí)候才會(huì)從數(shù)據(jù)庫(kù)中去查詢(xún) */ User user = (User)session.load(User.class, 2);
我們看到,如果我們僅僅是通過(guò)load來(lái)加載我們的User對(duì)象,此時(shí)從控制臺(tái)我們會(huì)發(fā)現(xiàn)并不會(huì)從數(shù)據(jù)庫(kù)中查詢(xún)出該對(duì)象,即并不會(huì)發(fā)出sql語(yǔ)句,但如果我們要使用該對(duì)象時(shí):
session = HibernateUtil.openSession(); User user = (User)session.load(User.class, 2); System.out.println(user);
此時(shí)我們看到控制臺(tái)會(huì)發(fā)出了sql查詢(xún)語(yǔ)句,會(huì)將該對(duì)象從數(shù)據(jù)庫(kù)中查詢(xún)出來(lái):
Hibernate: select user0_.id as id0_0_, user0_.username as username0_0_, user0_.password as password0_0_, user0_.born as born0_0_ from user user0_ where user0_.id=? User [id=2, username=aaa, password=111, born=2013-10-16 00:14:24.0]
這個(gè)時(shí)候我們可能會(huì)想,那么既然調(diào)用load方法時(shí),并不會(huì)發(fā)出sql語(yǔ)句去從數(shù)據(jù)庫(kù)中查出該對(duì)象,那么這個(gè)User對(duì)象到底是個(gè)什么對(duì)象呢?
其實(shí)這個(gè)User對(duì)象是我們的一個(gè)代理對(duì)象,這個(gè)代理對(duì)象僅僅保存了id這個(gè)屬性:
session = HibernateUtil.openSession(); /* * 通過(guò)load的方式加載對(duì)象時(shí),會(huì)使用延遲加載機(jī)制,此時(shí)得到的User對(duì)象其實(shí)是一個(gè)
* 代理對(duì)象,該代理對(duì)象里面僅僅只有id這個(gè)屬性 */ User user = (User)session.load(User.class, 2);
System.out.println(user.getId());
console: 2 我們看到,如果我們只打印出這個(gè)user對(duì)象的id值時(shí),此時(shí)控制臺(tái)會(huì)打印出該id值,但是同樣不會(huì)發(fā)出sql語(yǔ)句去從數(shù)據(jù)庫(kù)中去查詢(xún)。這就印證了我們的這個(gè)user對(duì)象僅僅是一個(gè)保存了id的代理對(duì)象,但如果我需要打印出user對(duì)象的其他屬性值時(shí),這個(gè)時(shí)候會(huì)不會(huì)發(fā)出sql語(yǔ)句呢?答案是肯定的:
session = HibernateUtil.openSession(); /* * 通過(guò)load的方式加載對(duì)象時(shí),會(huì)使用延遲加載機(jī)制,此時(shí)得到的User對(duì)象其實(shí)是一個(gè)
* 代理對(duì)象,該代理對(duì)象里面僅僅只有id這個(gè)屬性 */ User user = (User)session.load(User.class, 2);
System.out.println(user.getId()); // 如果此時(shí)要得到user其他屬性,則會(huì)從數(shù)據(jù)庫(kù)中查詢(xún)
System.out.println(user.getUsername()); 此時(shí)我們看控制臺(tái)的輸出:
2 Hibernate: select user0_.id as id0_0_, user0_.username as username0_0_, user0_.password as password0_0_, user0_.born as born0_0_ from user user0_ where user0_.id=? aaa
相信通過(guò)上述的幾個(gè)例子,大家應(yīng)該很好的了解了load的這種加載對(duì)象的方式了吧。
二、get加載方式
相對(duì)于load的延遲加載方式,get就直接的多,當(dāng)我們使用session.get()方法來(lái)得到一個(gè)對(duì)象時(shí),不管我們使不使用這個(gè)對(duì)象,此時(shí)都會(huì)發(fā)出sql語(yǔ)句去從數(shù)據(jù)庫(kù)中查詢(xún)出來(lái):
session = HibernateUtil.openSession(); /* * 通過(guò)get方法來(lái)加載對(duì)象時(shí),不管使不使用該對(duì)象,都會(huì)發(fā)出sql語(yǔ)句,從數(shù)據(jù)庫(kù)中查詢(xún) */ User user = (User)session.get(User.class, 2);
此時(shí)我們通過(guò)get方式來(lái)得到user對(duì)象,但是我們并沒(méi)有使用它,但是我們發(fā)現(xiàn)控制臺(tái)會(huì)輸出sql的查詢(xún)語(yǔ)句:
Hibernate: select user0_.id as id0_0_, user0_.username as username0_0_, user0_.password as password0_0_, user0_.born as born0_0_ from user user0_ where user0_.id=?
因此我們可以看到,使用load的加載方式比get的加載方式性能要好一些,因?yàn)閘oad加載時(shí),得到的只是一個(gè)代理對(duì)象,當(dāng)真正需要使用這個(gè)對(duì)象時(shí)再去從數(shù)據(jù)庫(kù)中查詢(xún)。
三、使用get和load時(shí)的一些小問(wèn)題
當(dāng)了解了load和get的加載機(jī)制以后,我們此時(shí)來(lái)看看這兩種方式會(huì)出現(xiàn)的一些小問(wèn)題:
①如果使用get方式來(lái)加載對(duì)象,當(dāng)我們?cè)噲D得到一個(gè)id不存在的對(duì)象時(shí),此時(shí)會(huì)報(bào)NullPointException的異常
session = HibernateUtil.openSession(); /* * 當(dāng)通過(guò)get方式試圖得到一個(gè)id不存在的user對(duì)象時(shí),此時(shí)會(huì)報(bào)NullPointException異常 */ User user = (User)session.get(User.class, 20);
System.out.println(user.getUsername());此時(shí)我們看控制臺(tái)的輸出信息,會(huì)報(bào)空指針的異常:
Hibernate: select user0_.id as id0_0_, user0_.username as username0_0_, user0_.password as password0_0_, user0_.born as born0_0_ from user user0_ where user0_.id=? java.lang.NullPointerException .........
這是因?yàn)橥ㄟ^(guò)get方式我們會(huì)去數(shù)據(jù)庫(kù)中查詢(xún)出該對(duì)象,但是這個(gè)id值不存在,所以此時(shí)user對(duì)象是null,所以就會(huì)報(bào)NullPointException的異常了。
②如果使用load方式來(lái)加載對(duì)象,當(dāng)我們?cè)噲D得到一個(gè)id不存在的對(duì)象時(shí),此時(shí)會(huì)報(bào)ObjectNotFoundException異常:
session = HibernateUtil.openSession(); /* * 當(dāng)通過(guò)get方式試圖得到一個(gè)id不存在的user對(duì)象時(shí),此時(shí)會(huì)報(bào)ObjectNotFoundException異常 */ User user = (User)session.load(User.class, 20);
System.out.println(user.getId());
System.out.println(user.getUsername());我們看看控制臺(tái)的輸出:
20 Hibernate: select user0_.id as id0_0_, user0_.username as username0_0_, user0_.password as password0_0_, user0_.born as born0_0_ from user user0_ where user0_.id=? org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.xiaoluo.bean.User#20]......
為什么使用load的方式和get的方式來(lái)得到一個(gè)不存在的對(duì)象報(bào)的異常不同呢??其原因還是因?yàn)閘oad的延遲加載機(jī)制,使用load時(shí),此時(shí)的user對(duì)象是一個(gè)代理對(duì)象,僅僅保存了當(dāng)前的這個(gè)id值,當(dāng)我們?cè)噲D得到該對(duì)象的username屬性時(shí),這個(gè)屬性其實(shí)是不存在的,所以就會(huì)報(bào)出ObjectNotFoundException這個(gè)異常了。
③org.hibernate.LazyInitializationException異常
接下來(lái)我們?cè)賮?lái)看一個(gè)例子:
public class UserDAO
{ public User loadUser(int id)
{
Session session = null;
Transaction tx = null;
User user = null; try {
session = HibernateUtil.openSession();
tx = session.beginTransaction();
user = (User)session.load(User.class, 1);
tx.commit();
} catch (Exception e)
{
e.printStackTrace();
tx.rollback();
} finally {
HibernateUtil.close(session);
} return user;
}
} @Test public void testLazy06()
{
UserDAO userDAO = new UserDAO();
User user = userDAO.loadUser(2);
System.out.println(user);
}模擬了一個(gè)UserDAO這樣的對(duì)象,然后我們?cè)跍y(cè)試用例里面來(lái)通過(guò)load加載一個(gè)對(duì)象,此時(shí)我們發(fā)現(xiàn)控制臺(tái)會(huì)報(bào)LazyInitializationException異常
org.hibernate.LazyInitializationException: could not initialize proxy - no Session .............
這個(gè)異常是什么原因呢??還是因?yàn)閘oad的延遲加載機(jī)制,當(dāng)我們通過(guò)load()方法來(lái)加載一個(gè)對(duì)象時(shí),此時(shí)并沒(méi)有發(fā)出sql語(yǔ)句去從數(shù)據(jù)庫(kù)中查詢(xún)出該對(duì)象,當(dāng)前這個(gè)對(duì)象僅僅是一個(gè)只有id的代理對(duì)象,我們還并沒(méi)有使用該對(duì)象,但是此時(shí)我們的session已經(jīng)關(guān)閉了,所以當(dāng)我們?cè)跍y(cè)試用例中使用該對(duì)象時(shí)就會(huì)報(bào)LazyInitializationException這個(gè)異常了。
所以以后我們只要看到控制臺(tái)報(bào)LazyInitializationException這種異常,就知道是使用了load的方式延遲加載一個(gè)對(duì)象了,解決這個(gè)的方法有兩種,一種是將load改成get的方式來(lái)得到該對(duì)象,另一種是在表示層來(lái)開(kāi)啟我們的session和關(guān)閉session。
至此,hibernate的兩種加載方式get和load已經(jīng)分析完畢?。?!
到此這篇關(guān)于Java hibernate延遲加載get和load的區(qū)別的文章就介紹到這了,更多相關(guān)Java hibernate 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java實(shí)現(xiàn)順序結(jié)構(gòu)線性列表的函數(shù)代碼
java實(shí)現(xiàn)順序結(jié)構(gòu)線性列表的函數(shù)代碼。需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-10-10
Springboot+Shiro+Mybatis+mysql實(shí)現(xiàn)權(quán)限安全認(rèn)證的示例代碼
Shiro是Apache?的一個(gè)強(qiáng)大且易用的Java安全框架,執(zhí)行身份驗(yàn)證、授權(quán)、密碼學(xué)和會(huì)話管理,Shiro?主要分為兩個(gè)部分就是認(rèn)證和授權(quán)兩部分,這篇文章主要介紹了Springboot+Shiro+Mybatis+mysql實(shí)現(xiàn)權(quán)限安全認(rèn)證的示例代碼,需要的朋友可以參考下2024-07-07
淺談HBase在SpringBoot項(xiàng)目里的應(yīng)用(含HBaseUtil工具類(lèi))
這篇文章主要介紹了淺談HBase在SpringBoot項(xiàng)目里的應(yīng)用(含HBaseUtil工具類(lèi)),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10
SpringBoot導(dǎo)出PDF的四種實(shí)現(xiàn)方法詳解
在Spring?Boot應(yīng)用程序中實(shí)現(xiàn)PDF導(dǎo)出功能,可以選擇多種庫(kù)和技術(shù)棧,本文為大家整理了四種常見(jiàn)的方法,感興趣的小伙伴可以參考一下2025-02-02
Java使用ES?Client?調(diào)用滾動(dòng)查詢(xún)及Elasticsearch滾動(dòng)查詢(xún)Scrolling機(jī)制
Elasticsearch提供了一種稱(chēng)為"滾動(dòng)查詢(xún)"(Scrolling)的機(jī)制,用于處理大型數(shù)據(jù)集的分頁(yè)查詢(xún),這篇文章給大家介紹滾動(dòng)查詢(xún)的一般步驟及Java使用ESClient調(diào)用滾動(dòng)查詢(xún)的方法,感興趣的朋友一起看看吧2023-08-08
詳解spring項(xiàng)目中如何動(dòng)態(tài)刷新bean
這篇文章主要為大家介紹了詳解spring項(xiàng)目中如何動(dòng)態(tài)刷新bean,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
SpringBoot集成logback打印彩色日志的代碼實(shí)現(xiàn)
Logback是由log4j創(chuàng)始人設(shè)計(jì)的另一個(gè)開(kāi)源日志組件,默認(rèn)情況下,Spring?Boot會(huì)用Logback來(lái)記錄日志,并用INFO級(jí)別輸出到控制臺(tái),本文給大家介紹了SpringBoot集成logback打印彩色日志,需要的朋友可以參考下2024-03-03
不規(guī)范使用ThreadLocal導(dǎo)致bug分析解決
這篇文章主要為大家介紹了不規(guī)范使用ThreadLocal導(dǎo)致bug分析解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01

