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