「Mybatis系列」Mybatis高級應(yīng)用

1. 關(guān)聯(lián)查詢

舉例:因為一個訂單信息只會是一個人下的訂單邢享,所以從查詢訂單信息出發(fā),關(guān)聯(lián)查詢用戶信息為一對一查詢淡诗。如果從用戶信息出發(fā)骇塘,查詢用戶下的訂單信息則為一對多查詢,因為一個用戶可以下多個訂單韩容。

1.1 一對一查詢

需求

查詢所有訂單信息款违,關(guān)聯(lián)查詢下單用戶信息。

SQL語句

主信息:訂單表

從信息:用戶表

SELECT?

??orders.*,

??user.username,

??user.address

FROM

??orders?LEFT?JOIN?user?

??ON?orders.user_id?=?user.id

方法一:resultType

返回resultType方式比較簡單宙攻,也比較常用奠货,就不做介紹了。

方法二:resultMap

使用resultMap進行結(jié)果映射座掘,定義專門的resultMap用于映射一對一查詢結(jié)果递惋。

創(chuàng)建擴展po類

創(chuàng)建OrdersExt類(該類用于結(jié)果集封裝)柔滔,加入User屬性,user屬性中用于存儲關(guān)聯(lián)查詢的用戶信息萍虽,因為訂單關(guān)聯(lián)查詢用戶是一對一關(guān)系睛廊,所以這里使用單個User對象存儲關(guān)聯(lián)查詢的用戶信息。

public?classOrdersExtextendsOrders{

????private?User?user;//?用戶對象

????//?get/set杉编。超全。。邓馒。

}

Mapper映射文件

在UserMapper.xml中嘶朱,添加以下代碼:

<!--?查詢訂單關(guān)聯(lián)用戶信息使用resultmap?-->

<!--?一對一關(guān)聯(lián)映射?-->

property:Orders對象的user屬性

javaType:user屬性對應(yīng)?的類型

-->

<!--?column:user表的主鍵對應(yīng)的列??property:user對象中id屬性-->

SELECT

o.id,

o.user_id,

o.number,

o.createtime,

o.note,

u.username,

u.address

FROM

orders?o

JOIN?`user`?u?ON?u.id?=?o.user_id

association:表示進行一對一關(guān)聯(lián)查詢映射

property:表示關(guān)聯(lián)查詢的結(jié)果存儲在com.kkb.mybatis.po.Orders的user屬性中

javaType:表示關(guān)聯(lián)查詢的映射結(jié)果類型

Mapper接口

在UserMapper接口中,添加以下接口方法:

publicListfindOrdersAndUserRstMap()throwsException;

測試代碼

在UserMapperTest測試類中光酣,添加測試代碼:

publicvoidtestfindOrdersAndUserRstMap()throwsException{

//獲取session

SqlSession?session?=?sqlSessionFactory.openSession();

//獲限mapper接口實例

UserMapper?userMapper?=?session.getMapper(UserMapper.class);

//查詢訂單信息

List?list?=?userMapper.findOrdersAndUserRstMap();

System.out.println(list);

//關(guān)閉session

session.close();

}

小結(jié)

使用resultMap進行結(jié)果映射時疏遏,具體是使用association完成關(guān)聯(lián)查詢的映射,將關(guān)聯(lián)查詢信息映射到pojo對象中救军。

1.2 一對多查詢

需求

查詢所有用戶信息及用戶關(guān)聯(lián)的訂單信息财异。

SQL語句

主信息:用戶信息

從信息:訂單信息

SELECT

????u.*,?

????o.id?oid,

????o.number,

????o.createtime,

????o.note

FROM

????`user`?u

LEFT?JOIN?orders?o?ON?u.id?=?o.user_id

分析

在一對多關(guān)聯(lián)查詢時,只能使用resultMap進行結(jié)果映射:

1唱遭、一對多關(guān)聯(lián)查詢時戳寸,sql查詢結(jié)果有多條,而映射對象是一個拷泽。

2疫鹊、resultType完成結(jié)果映射的方式的一條記錄映射一個對象。

3跌穗、resultMap完成結(jié)果映射的方式是以[主信息]為主對象订晌,[從信息]映射為集合或者對象虏辫,然后封裝到主對象中蚌吸。

修改po類

在User類中加入List orders屬性。

Mapper映射文件

在UserMapper.xml文件中砌庄,添加以下代碼:

<!--?用戶信息映射?-->

<!--?一對多關(guān)聯(lián)映射?-->

SELECT

u.*,

o.id?oid,

o.number,

o.createtime,

o.note

FROM

`user`?u

LEFT?JOIN?orders?o?ON?u.id?=?o.user_id

Collection標(biāo)簽:定義了一對多關(guān)聯(lián)的結(jié)果映射羹唠。

property="orders":關(guān)聯(lián)查詢的結(jié)果集存儲在User對象的上哪個屬性。

ofType="orders":指定關(guān)聯(lián)查詢的結(jié)果集中的對象類型即List中的對象類型娄昆。此處可以使用別名佩微,也可以使用全限定名。

Mapper接口

//?resultMap入門

publicListfindUserAndOrdersRstMap()throwsException;

測試代碼

@Test

publicvoidtestFindUserAndOrdersRstMap(){

SqlSession?session?=?sqlSessionFactory.openSession();

UserMapper?userMapper?=?session.getMapper(UserMapper.class);

List?result?=?userMapper.findUserAndOrdersRstMap();

for(User?user?:?result)?{

System.out.println(user);

}

session.close();

}

2. 延遲加載

2.1 什么是延遲加載

MyBatis中的延遲加載萌焰,也稱為懶加載哺眯,是指在進行關(guān)聯(lián)查詢時,按照設(shè)置延遲規(guī)則推遲對關(guān)聯(lián)對象的select查詢扒俯。延遲加載可以有效的減少數(shù)據(jù)庫壓力奶卓。

Mybatis的延遲加載一疯,需要通過resultMap標(biāo)簽中的association和collection子標(biāo)簽才能演示成功。

Mybatis的延遲加載夺姑,也被稱為是嵌套查詢墩邀,對應(yīng)的還有嵌套結(jié)果的概念,可以參考一對多關(guān)聯(lián)的案例盏浙。

注意:MyBatis的延遲加載只是對關(guān)聯(lián)對象的查詢有延遲設(shè)置眉睹,對于主加載對象都是直接執(zhí)行查詢語句的sql

2.2 延遲加載的分類

MyBatis根據(jù)對關(guān)聯(lián)對象查詢的select語句的執(zhí)行時機废膘,分為三種類型:直接加載竹海、侵入式加載與深度延遲加載

直接加載:執(zhí)行完對主加載對象的select語句,馬上執(zhí)行對關(guān)聯(lián)對象的select查詢丐黄。

侵入式延遲:執(zhí)行對主加載對象的查詢時站削,不會執(zhí)行對關(guān)聯(lián)對象的查詢。但當(dāng)要訪問主加載對象的某個屬性(該屬性不是關(guān)聯(lián)對象的屬性)時孵稽,就會馬上執(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查詢接校。

延遲加載策略需要在Mybatis的全局配置文件中猛频,通過標(biāo)簽進行設(shè)置。

2.3 案例準(zhǔn)備

查詢訂單信息及它的下單用戶信息蛛勉。

2.4 直接加載

通過對全局參數(shù):lazyLoadingEnabled進行設(shè)置鹿寻,默認(rèn)就是false。

<settings>????

<!--?延遲加載總開關(guān)?-->???

?<setting?name="lazyLoadingEnabled"?value="false"/></settings>

????2.5 侵入式延遲加載

<settings>??

??<!--?延遲加載總開關(guān)?-->??

??<setting?name="lazyLoadingEnabled"?value="true"/>??

??<!--?侵入式延遲加載開關(guān)?-->???

?<setting?name="aggressiveLazyLoading"?value="true"/></settings>

2.6 深度延遲加載

<settings>???

?<!--?延遲加載總開關(guān)?-->??

??<setting?name="lazyLoadingEnabled"?value="true"/>??

??<!--?侵入式延遲加載開關(guān)?-->???

?<setting?name="aggressiveLazyLoading"?value="false"/></settings>

2.7 N+1問題

深度延遲加載的使用會提升性能诽凌。

如果延遲加載的表數(shù)據(jù)太多毡熏,此時會產(chǎn)生N+1問題,主信息加載一次算1次侣诵,而從信息是會根據(jù)主信息傳遞過來的條件痢法,去查詢從表多次。

3. 動態(tài)SQL

動態(tài)SQL的思想:就是使用不同的動態(tài)SQL標(biāo)簽去完成字符串的拼接處理杜顺、循環(huán)判斷财搁。

解決的問題是:

在映射文件中,會編寫很多有重疊部分的SQL語句躬络,比如SELECT語句和WHERE語句等這些重疊語句尖奔,該如何處理

SQL語句中的where條件有多個,但是頁面只傳遞過來一個條件參數(shù),此時會發(fā)生問題提茁。

3.1 if標(biāo)簽

綜合查詢的案例中仗嗦,查詢條件是由頁面?zhèn)魅耄撁嬷械牟樵儣l件可能輸入用戶名稱甘凭,也可能不輸入用戶名稱稀拐。

?<select?id="findUserList"?parameterType="queryVo"?resultType="user">?????

???SELECT?*?FROM?user?where?1=1?????

???<if?test="user?!=?null">???????

????<if?test="user.username?!=?null?and?user.username?!=?''">??????????

??????AND?username?like?'%${user.username}%'??????

??????</if>????

????</if>??

??</select>

注意:要做『不等于空』字符串校驗。

3.2 where標(biāo)簽

上邊的sql中的1=1丹弱,雖然可以保證sql語句的完整性:但是存在性能問題德撬。Mybatis提供where標(biāo)簽解決該問題。

代碼修改如下:

?<select?id="findUserList"?parameterType="queryVo"?resultType="user">???

?????SELECT?*?FROM?user?????

???<!--?where標(biāo)簽會處理它后面的第一個and?-->??????

??<where>? ? ? ?

? ?<if?test="user?!=?null">?????

???????????<if?test="user.username?!=?null?and?user.username?!=?''">???????

?????????????AND?username?like?'%${user.username}%'???????????

?????</if>???????

?????</if>????

????</where>???

?</select>

3.3 sql片段

在映射文件中可使用sql標(biāo)簽將重復(fù)的sql提取出來躲胳,然后使用include標(biāo)簽引用即可蜓洪,最終達到sql重用的目的,具體實現(xiàn)如下:

原映射文件中的代碼:

<select?id="findUserList"?parameterType="queryVo"?resultType="user">??

????SELECT?*?FROM?user??????<!--?where標(biāo)簽會處理它后面的第一個and?-->???

???<where>?????

?????<if?test="user?!=?null">??????

????????<if?test="user.username?!=?null?and?user.username?!=?''">????????

??????????AND?username?like?'%${user.username}%'????????

??????</if>???????

???</if>????????????

?????</where>?

?</select>

將where條件抽取出來:

<sql?id="query_user_where">?????

???<if?test="user?!=?null">??????

??????<if?test="user.username?!=?null?and?user.username?!=?''">???????????

?????AND?username?like?'%${user.username}%'?????

???????</if>?????

???</if>

</sql>

使用include引用:

<!--?使用包裝類型查詢用戶?使用ognl從對象中取屬性值坯苹,如果是包裝對象可以使用.操作符來取內(nèi)容部的屬性?-->???

?<select?id="findUserList"?parameterType="queryVo"?resultType="user">???

?????SELECT?*?FROM?user?????

???<!--?where標(biāo)簽會處理它后面的第一個and?-->??

??????<where>??????

??????<include?refid="query_user_where"></include>????

????</where>???

?</select>

注意:

1隆檀、如果引用其它mapper.xml的sql片段,則在引用時需要加上namespace粹湃,如下:

<include refid="namespace.sql片段”/>

3.4 foreach

需求

綜合查詢時恐仑,傳入多個id查詢用戶信息,用下邊兩個sql實現(xiàn):

SELECT?*?FROM?USER?WHERE?username?LIKE?'%老郭%'?AND?(id?=1?OR?id?=10?OR?id=16)SELECT?*?FROM?USER?WHERE?username?LIKE?'%老郭%'??AND??id??IN?(1,10,16)

POJO

在pojo中定義list屬性ids存儲多個用戶id为鳄,并添加getter/setter方法

Mapper映射文件

<sql?id="query_user_where">?????

???<if?test="user?!=?null">??????

??????<if?test="user.username?!=?null?and?user.username?!=?''">?????

??????????AND?username?like?'%${user.username}%'??????

??????</if>???

?????</if>????

????<if?test="ids?!=?null?and?ids.size()?>?0">?????

???????<!--?collection:指定輸入的集合參數(shù)的參數(shù)名稱?-->????

????????<!--?item:聲明集合參數(shù)中的元素變量名?-->????????

????<!--?open:集合遍歷時裳仆,需要拼接到遍歷sql語句的前面?-->??

??????????<!--?close:集合遍歷時,需要拼接到遍歷sql語句的后面?-->???????

?????<!--?separator:集合遍歷時孤钦,需要拼接到遍歷sql語句之間的分隔符號?-->??????????

??<foreach?collection="ids"?item="id"?open="?AND?id?IN?(?"??????

??????????close="?)?"?separator=",">??????

??????????#{id}??????

??????</foreach>???

?????</if>??

??</sql

測試代碼

在UserMapperTest測試代碼中歧斟,修改testFindUserList方法,如下:

@Test

publicvoidtestFindUserList()throwsException{

SqlSession?sqlSession?=?sqlSessionFactory.openSession();

//?獲得mapper的代理對象

UserMapper?userMapper?=?sqlSession.getMapper(UserMapper.class);

//?創(chuàng)建QueryVo對象

QueryVo?queryVo?=newQueryVo();

//?創(chuàng)建user對象

User?user?=newUser();

user.setUsername("老郭");

queryVo.setUser(user);

List?ids?=newArrayList();

ids.add(1);//?查詢id為1的用戶

ids.add(10);//?查詢id為10的用戶

queryVo.setIds(ids);

//?根據(jù)queryvo查詢用戶

List?list?=?userMapper.findUserList(queryVo);

System.out.println(list);

sqlSession.close();

}

注意事項

如果parameterType不是POJO類型偏形,而是List或者Array的話静袖,那么foreach語句中,collection屬性值需要固定寫死為list或者array俊扭。

以上內(nèi)容都是我自己的一些感想队橙,分享出來歡迎大家指正,順便求一波關(guān)注

作者:雙哥

出處:https://juejin.im/post/5e4f33526fb9a07c9645920e

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末统扳,一起剝皮案震驚了整個濱河市喘帚,隨后出現(xiàn)的幾起案子畅姊,更是在濱河造成了極大的恐慌咒钟,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,222評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件若未,死亡現(xiàn)場離奇詭異朱嘴,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,455評論 3 385
  • 文/潘曉璐 我一進店門萍嬉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來乌昔,“玉大人,你說我怎么就攤上這事壤追】牡溃” “怎么了?”我有些...
    開封第一講書人閱讀 157,720評論 0 348
  • 文/不壞的土叔 我叫張陵行冰,是天一觀的道長溺蕉。 經(jīng)常有香客問我,道長悼做,這世上最難降的妖魔是什么疯特? 我笑而不...
    開封第一講書人閱讀 56,568評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮肛走,結(jié)果婚禮上漓雅,老公的妹妹穿的比我還像新娘。我一直安慰自己朽色,他們只是感情好邻吞,可當(dāng)我...
    茶點故事閱讀 65,696評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著葫男,像睡著了一般吃衅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上腾誉,一...
    開封第一講書人閱讀 49,879評論 1 290
  • 那天徘层,我揣著相機與錄音,去河邊找鬼利职。 笑死趣效,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的猪贪。 我是一名探鬼主播跷敬,決...
    沈念sama閱讀 39,028評論 3 409
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼热押!你這毒婦竟也來了西傀?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,773評論 0 268
  • 序言:老撾萬榮一對情侶失蹤桶癣,失蹤者是張志新(化名)和其女友劉穎拥褂,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體牙寞,經(jīng)...
    沈念sama閱讀 44,220評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡饺鹃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,550評論 2 327
  • 正文 我和宋清朗相戀三年莫秆,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片悔详。...
    茶點故事閱讀 38,697評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡镊屎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出茄螃,到底是詐尸還是另有隱情缝驳,我是刑警寧澤,帶...
    沈念sama閱讀 34,360評論 4 332
  • 正文 年R本政府宣布归苍,位于F島的核電站党巾,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏霜医。R本人自食惡果不足惜齿拂,卻給世界環(huán)境...
    茶點故事閱讀 40,002評論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望肴敛。 院中可真熱鬧署海,春花似錦、人聲如沸医男。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,782評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽镀梭。三九已至刀森,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間报账,已是汗流浹背研底。 一陣腳步聲響...
    開封第一講書人閱讀 32,010評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留透罢,地道東北人榜晦。 一個月前我還...
    沈念sama閱讀 46,433評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像羽圃,于是被迫代替她去往敵國和親乾胶。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,587評論 2 350

推薦閱讀更多精彩內(nèi)容