Mybatis延遲加載

延遲加載的含義

延遲加載又叫按需查詢(懶加載)叶撒,mybatis支持延遲加載,我們希望一次性把常用的級聯(lián)數(shù)據(jù)通過sql直接查詢出來,而對于那些不常用的的級聯(lián)數(shù)據(jù)不要取出蛆橡,而是等待要用的時候才取出,這些不常用的級聯(lián)數(shù)據(jù)可以采用延遲加載的功能矢空。

延遲加載的配置

在mybatis的settings配置中存在兩個元素可以配置級聯(lián)

<caption style="margin: 0px; padding: 0px;">延遲加載的配置項(xiàng)</caption>
| 配置項(xiàng) | 作用 | 配置選項(xiàng)說明 | 默認(rèn)值 |
| lazyLoadingEnabled | 延遲加載的全局開關(guān)航罗。當(dāng)開啟時,所有關(guān)聯(lián)對象都會延遲加載屁药。在特定關(guān)聯(lián)關(guān)系中粥血,可通過設(shè)置fetchType屬性來覆蓋該項(xiàng)的開關(guān)狀態(tài) | true|false | false |
| aggressiveLazyLoading | 當(dāng)啟用時柏锄,對任意延遲屬性的調(diào)用會使帶有延遲加載屬性的對象完整加載;反之复亏,則每種屬性按需加載趾娃。 | true|false | 版本3.4.1(包含)之前為true,之后為false |

lazyLoadingEnabled表示延遲加載的總開關(guān)缔御,如果將其設(shè)置為false抬闷,即使侵入式開關(guān)設(shè)置為true也不會生效。

aggressiveLazyLoading表示侵入式延遲加載開關(guān)耕突,在3.4.1版本之前默認(rèn)是true笤成,之后默認(rèn)是false。

<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;"><settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="true"/>
</settings></pre>

** 延遲加載的優(yōu)缺點(diǎn)**

優(yōu)點(diǎn):先從單表查詢眷茁,需要時再從關(guān)聯(lián)表去關(guān)聯(lián)查詢炕泳,大大提高數(shù)據(jù)庫的性能,因?yàn)椴樵儐伪硪汝P(guān)聯(lián)查詢多張表的速度快很多上祈。

缺點(diǎn):因?yàn)橹挥挟?dāng)需要用到數(shù)據(jù)時培遵,才會進(jìn)行數(shù)據(jù)庫查詢,這樣在大批量數(shù)據(jù)查詢時登刺,因?yàn)椴樵児ぷ饕残枰馁M(fèi)時間籽腕,所以可能造成用戶等待時間變長,造成用戶體驗(yàn)下降纸俭。

加載時機(jī)

直接加載:執(zhí)行完對主加載對象的 select 語句皇耗,馬上執(zhí)行對關(guān)聯(lián)對象的 select 查詢。
侵入式延遲: 執(zhí)行對主加載對象的查詢時掉蔬,不會執(zhí)行對關(guān)聯(lián)對象的查詢廊宪。但當(dāng)要訪問主加載對象的詳情屬性時,就會馬上執(zhí)行關(guān)聯(lián)對象的select查詢女轿。
深度延遲: 執(zhí)行對主加載對象的查詢時箭启,不會執(zhí)行對關(guān)聯(lián)對象的查詢。訪問主加載對象的詳情時也不會執(zhí)行關(guān)聯(lián)對象的select查詢蛉迹。只有當(dāng)真正訪問關(guān)聯(lián)對象的詳情時傅寡,才會執(zhí)行對關(guān)聯(lián)對象的 select 查詢。

如何實(shí)現(xiàn)延遲加載

MyBatis中對于延遲加載設(shè)置北救,只對于resultMap中的collection和association起作用荐操,可以應(yīng)用到一對一、一對多珍策、多對一托启、多對多的所有關(guān)聯(lián)關(guān)系查詢中。

需要注意的是攘宙, 延遲加載的應(yīng)用要求屯耸,關(guān)聯(lián)對象的查詢與主加載對象的查詢必須是分別進(jìn)行的 select 語句拐迁,不能是使用多表連接所進(jìn)行的select查詢。因?yàn)槎啾磉B接查詢疗绣,其實(shí)質(zhì)是對一張表的查詢线召,對由多個表連接后形成的一張表的查詢。會一次性將多張表的所有信息查詢出來多矮。

一對多的多表單獨(dú)查詢方式

修改之前的mapper.xml文件

<!--根據(jù)team的id查找player-->
<select id="selectPlayerByTeamId" resultType="Player"> select id,name from t_player WHERE tid=#{id} </select>

<!--關(guān)聯(lián)屬性映射關(guān)系-->
<!--集合的數(shù)據(jù)來自select查詢缓淹,該查詢的條件是selectTeamByIdAlone查詢出的id-->
<resultMap id="teamMapAlone" type="Team">
    <id column="id" property="id"/>
    <result column="name" property="name"/>
    <collection property="playerList" ofType="Player" select="selectPlayerByTeamId" column="id"/>
</resultMap>

<select id="selectTeamByIdAlone" resultMap="teamMapAlone"> SELECT id,name FROM t_team where id=#{id} </select></pre>

修改之前的dao接口和測試類中的方法:

//dao接口
Team selectTeamByIdAlone(int id); //測試類
@Test public void selectTeamByIdAlone() {
    Team team = teamDao.selectTeamByIdAlone(1);
}

執(zhí)行之后,可以看到在控制臺中分兩次發(fā)出了sql語句分別查詢t_team和t_player表塔逃。

開啟侵入式延遲

在mybatis.xml文件中添加下面內(nèi)容讯壶,注意該內(nèi)容的位置一定要在properties與typeAliases之間,在mybatis.xml配置文件中患雏,有些標(biāo)簽是需要按照順序編寫的:

<!--全局參數(shù)設(shè)置-->
<settings>
    <!--延遲加載總開關(guān)-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--侵入式延遲加載開關(guān)-->
    <!--3.4.1版本之前默認(rèn)是true鹏溯,之后默認(rèn)是false-->
    <setting name="aggressiveLazyLoading" value="true"/>
</settings>

將以上內(nèi)容配置好之后,再執(zhí)行上面測試類方法的時候淹仑,就會發(fā)現(xiàn)在控制臺中只發(fā)出了一條查詢t_team的sql語句,這是因?yàn)槲覀冮_啟了侵入式延遲加載開關(guān)肺孵,在java程序中并未訪問Team中的任何屬性匀借,所以mybatis不會去查詢其關(guān)聯(lián)的player對象數(shù)據(jù)。

修改后測試方法:

@Test public void selectTeamByIdAlone() {
    Team team = teamDao.selectTeamByIdAlone(1);
    System.out.println(team.getName());
}

再次執(zhí)行上面方法之后就會看到控制臺中發(fā)出兩條SQL語句分別查詢t_team和t_player,這是因?yàn)樵趈ava程序中訪問了Team的屬性name平窘,所以mybatis會將其關(guān)聯(lián)的player對象數(shù)據(jù)查詢出來吓肋。

開啟深度延遲加載

修改mybatis.xml文件:開啟總開關(guān),將aggressiveLazyLoading關(guān)閉即可瑰艘。

<!--全局參數(shù)設(shè)置-->
<settings>
    <!--延遲加載總開關(guān)-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--侵入式延遲加載開關(guān)-->
    <!--3.4.1版本之前默認(rèn)是true是鬼,之后默認(rèn)是false-->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

繼續(xù)執(zhí)行上面的測試方法,可以看到控制臺中只打印了一條查詢t_team表的sql語句紫新。

修改測試方法如下:

@Test public void selectTeamByIdAlone() {
    Team team = teamDao.selectTeamByIdAlone(1);
    System.out.println(team.getName());
    System.out.println(team.getPlayerList().size());
}</pre>

執(zhí)行上面測試方法之后均蜜,可以看到控制臺中打印了兩條sql語句分別查詢t_team和t_player,這說明當(dāng)開啟深度延遲后芒率,只要代碼中不使用player相關(guān)的數(shù)據(jù)囤耳,mybatis就不會進(jìn)行sql查詢,只有當(dāng)真正使用的時候才會去發(fā)出sql語句查詢偶芍。

在單個resultMap中使用延遲加載(fetchType)

上面都是通過在mybatis.xml文件中統(tǒng)一配置的深度延遲加載充择,倘若只希望某些查詢支持深度延遲加載的話可以在resultMap中的collection或association添加fetchType屬性,配置為lazy之后是開啟深度延遲匪蟀,配置eager是不開啟深度延遲椎麦。fetchType屬性將取代全局配置參數(shù)lazyLoadingEnabled的設(shè)置

延遲加載總結(jié)

通過上面的示例可以發(fā)現(xiàn)深度加載的方式最為懶,通過這種方式可以讓mybatis在執(zhí)行查詢的時候減少sql的查詢從而提高程序的執(zhí)行效率材彪,但是并不是所有場景下使用懶加載都能提高效率观挎,有些場景比如在查詢一對多時琴儿,就需要將一方和多方都查詢出來,這樣的話開啟懶加載反而有可能會拖慢程序的執(zhí)行效率


mybatis的延遲加載就是按需查詢键兜,在需要的時候進(jìn)行查詢凤类。

有兩張表:

圖書表(book):

image

圖書類型表(category):

image

他們之間通過類型id進(jìn)行關(guān)聯(lián),現(xiàn)在我要顯示圖書類型名普气,點(diǎn)擊類型名再顯示該類型下的所有圖書谜疤。

我們可以這樣做在類型實(shí)體類里面添加一個屬性存放該類型下的圖書

public class Category {
    private int cid;
    private String cname;
    private List<Book> books;
//省略get set
}

一次性的把圖書類型和圖書查詢出來,Sql語句如下:

SELECT book.*,cname FROM book,category WHERE book.cid = category.cid

這樣做可以完成功能现诀,但是我們只是需要顯示圖書類型夷磕,點(diǎn)擊的時候才顯示該類型的圖書,如果能做到開始只查詢類型仔沿,點(diǎn)擊類型的時候再查詢該類型的圖書坐桩,就不需要進(jìn)行兩表聯(lián)查了,可以提高查詢的效率封锉,也比較節(jié)省內(nèi)存绵跷,這就是延遲加載。

延遲加載如何實(shí)現(xiàn)成福?

1. Category實(shí)體類同上

2. UserDao.xml:

<mapper namespace="cn.xh.dao.UserDao">
    <select id="findCategoryWithLazingload" resultMap="categoryMap">
        select * from category
    </select>
    <resultMap id="categoryMap" type="cn.xh.pojo.Category">
        <id column="cid" property="cid"></id>
        <result column="cname" property="cname"></result>

        <collection property="books" column="cid" select="findBookWithLazy"></collection>
    </resultMap>

    <select id="findBookWithLazy" parameterType="int" resultType="cn.xh.pojo.Book">
        select * from book where cid = #{cid}
    </select>
</mapper>

只有我們點(diǎn)擊類型的時候才需要查詢該類型下的圖書碾局,所以這里我們沒有用兩表聯(lián)查,而是將類型表的查詢語句和圖書表的查詢語句分開奴艾。

重點(diǎn)來看下這個配置:

<collection property="books" column="cid" select="findBookWithLazy"></collection>

collection净当,association是支持延遲加載的,這里的select屬性表示要執(zhí)行的sql語句蕴潦,column表示執(zhí)行sql語句要傳的參數(shù)像啼,該參數(shù)為select *** **from category查詢出來的字段cid,property=”books”表示查詢出來的結(jié)果給到books屬性。

  1. 在mybatis的核心配置文件中配置
<settings>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"></setting>
</settings>

注意潭苞,這個配置必須寫在properties配置的后面忽冻,typeAliases的前面。

將lazyLoadingEnabled設(shè)置為true表示開啟延遲加載萄传,默認(rèn)為false.

將aggressiveLazyLoading設(shè)置為false表示按需加載甚颂,默認(rèn)為true

關(guān)于這兩個設(shè)置后面再詳加探討

4. 測試:

@Test
public void testDo(){
    SqlSession session = sqlSessionFactory.openSession();
    UserDao u =  session.getMapper(UserDao.class);//用動態(tài)代理的方式自動生成接口的實(shí)現(xiàn)類
 List<Category> lst  =  u.findCategoryWithLazingload();
 for (Category c : lst) {
        System.out.println(c.getCname());

    }
    session.close();
}

執(zhí)行看看日志:

image

這段代碼只涉及到了圖書類型秀菱,并未涉及到圖書部分振诬,所以只執(zhí)行了select * from category從類型表中查詢出類型信息。

再來測試這段代碼:

@Test
public void testDo1(){
    SqlSession session = sqlSessionFactory.openSession();
    UserDao u =  session.getMapper(UserDao.class);//用動態(tài)代理的方式自動生成接口的實(shí)現(xiàn)類
 List<Category> lst  =  u.findCategoryWithLazingload();
 for (Category c : lst) {
        System.out.println(c.getCname());
    }
    List<Book> lstBook = lst.get(0).getBooks();
    session.close();
}

執(zhí)行看看日志:

image

這里執(zhí)行了兩個sql語句:

Select * from category

Select * from book where cid = ?

對比這兩段代碼衍菱,可以看到赶么, 只有當(dāng)執(zhí)行List<Book> lstBook = lst.get(0).getBooks();這行代碼的時候才會去執(zhí)行sql語句Select * from book where cid = ?。

這就是延遲加載里的按需執(zhí)行sql語句脊串,只有在需要的時候才會去執(zhí)行辫呻。

回顧一下第三個步驟中的配置:

此時 lazyLoadingEnabled設(shè)置為true, aggressiveLazyLoading設(shè)置為false清钥,表示延遲加載開啟,按需加載也開啟放闺。分析一下在這種配置下代碼的每一步都做了什么:

1.List<Category> lst = u.findCategoryWithLazingload();執(zhí)行到這行代碼的時候從數(shù)據(jù)庫中查詢圖書類型的信息祟昭。

2.System.out.println(c.getCname());執(zhí)行這行代碼的時候因?yàn)閳D書類型信息已經(jīng)被查詢出來,所以不需要再和數(shù)據(jù)庫交互怖侦。

3.List<Book> lstBook = lst.get(0).getBooks();執(zhí)行這行代碼的時候從數(shù)據(jù)庫中查詢該類型下圖書的信息篡悟。

如果將lazyLoadingEnabled設(shè)置為true, aggressiveLazyLoading設(shè)置為true,表示延遲加載開啟,按需加載關(guān)閉匾寝,代碼每一步都做了什么呢搬葬?

1.List<Category> lst = u.findCategoryWithLazingload();執(zhí)行到這行代碼的時候從數(shù)據(jù)庫中查詢圖書類型的信息。

2.System.out.println(c.getCname());執(zhí)行這行代碼的時候艳悔,需要加載圖書類型的屬性“類型名”急凰,因?yàn)閷葱杓虞d關(guān)閉,所以此時會把Category的所有屬性都加載進(jìn)來猜年,包括List<Book> books,會去數(shù)據(jù)庫中查詢圖書的信息抡锈。

3.List<Bosok> lstBook = lst.get(0).getBooks();執(zhí)行這行代碼的時候因?yàn)閳D書的信息已經(jīng)被加載進(jìn)來,不需要查詢數(shù)據(jù)庫乔外。

如果將lazyLoadingEnabled設(shè)置為false企孩,相當(dāng)于關(guān)閉了延遲加載,此時無論aggressiveLazyLoading是true還是false都會在執(zhí)行

List<Category> lst = u.findCategoryWithLazingload();

的時候?qū)㈩愋秃蛨D書的信息都查詢出來袁稽。

總結(jié)一下:

一:延遲加載就是按需加載,在需要查詢的時候再去查詢擒抛,使用延遲加載可以避免表連接查詢推汽,表連接查詢比單表查詢的效率低,但是它需要多次與數(shù)據(jù)庫進(jìn)行交互歧沪,所以延遲加載并不是銀彈歹撒,使用需謹(jǐn)慎。

二:關(guān)于延遲加載有兩個重要的設(shè)置:lazyLoadingEnabled表示延遲加載是否開啟诊胞,如果設(shè)置為true表示開啟暖夭,此時還需要設(shè)置aggressiveLazyLoading為false,才能做到按需加載,如果aggressiveLazyLoading設(shè)置為true則按需加載關(guān)閉撵孤,此時只要加載了某個屬性就會將所有屬性都加載迈着。

lazyLoadingEnabled的默認(rèn)值為false

aggressiveLazyLoading的默認(rèn)值為true

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市邪码,隨后出現(xiàn)的幾起案子裕菠,更是在濱河造成了極大的恐慌,老刑警劉巖闭专,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奴潘,死亡現(xiàn)場離奇詭異旧烧,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)画髓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門掘剪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人奈虾,你說我怎么就攤上這事夺谁。” “怎么了愚墓?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵予权,是天一觀的道長。 經(jīng)常有香客問我浪册,道長扫腺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任村象,我火速辦了婚禮笆环,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘厚者。我一直安慰自己躁劣,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布库菲。 她就那樣靜靜地躺著账忘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪熙宇。 梳的紋絲不亂的頭發(fā)上鳖擒,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天,我揣著相機(jī)與錄音烫止,去河邊找鬼蒋荚。 笑死,一個胖子當(dāng)著我的面吹牛馆蠕,可吹牛的內(nèi)容都是我干的期升。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼互躬,長吁一口氣:“原來是場噩夢啊……” “哼播赁!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起吨铸,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤行拢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體舟奠,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡竭缝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了沼瘫。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抬纸。...
    茶點(diǎn)故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖耿戚,靈堂內(nèi)的尸體忽然破棺而出湿故,到底是詐尸還是另有隱情,我是刑警寧澤膜蛔,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布坛猪,位于F島的核電站,受9級特大地震影響皂股,放射性物質(zhì)發(fā)生泄漏墅茉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一呜呐、第九天 我趴在偏房一處隱蔽的房頂上張望就斤。 院中可真熱鬧,春花似錦蘑辑、人聲如沸洋机。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绷旗。三九已至,卻和暖如春副砍,著一層夾襖步出監(jiān)牢的瞬間刁标,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工址晕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人顿锰。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓谨垃,卻偏偏與公主長得像,于是被迫代替她去往敵國和親硼控。 傳聞我的和親對象是個殘疾皇子刘陶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評論 2 348