【MyBatis】MyBatis多表操作

MyBatis多表操作

前言

在前面的兩個小節(jié)里,我們已經(jīng)初步接觸到MyBatis,并且通過MyBatis實現(xiàn)了單表的增刪改查操作,但在實際開發(fā)過程中浸遗,經(jīng)常遇到的是多表之間的操作,MyBatis在多表操作方面也提供非常方便的工具用于將結果集映射到對象中箱亿,這一節(jié)跛锌,我們將詳細學習這一部分。

多表操作

由于本節(jié)涉及到多表操作,在前面建立的數(shù)據(jù)表明顯不符合髓帽,所以這里我們需要再建立一些表以及插入一些數(shù)據(jù)

本節(jié)所使用的表以及數(shù)據(jù)均來自劉增輝老師的《MyBatis從入門到精通》


create table sys_user (
  id bigint not null auto_increment comment '用戶ID',
  user_name varchar(50) comment '用戶名',
  user_password varchar(50) comment '密碼',
  user_email varchar(50) comment '郵箱',
  create_time datetime comment '創(chuàng)建時間',
  primary key (id)
);
alter table sys_user comment '用戶表';

create table sys_role (
  id bigint not null auto_increment comment '角色ID',
  role_name varchar(50) comment '角色名',
  enabled int comment '有效標志',
  create_by bigint comment '創(chuàng)建人',
  create_time datetime comment '創(chuàng)建時間',
  primary key (id)
);
alter table sys_role comment '角色表';

create table sys_privilege (
  id bigint not null  auto_increment comment '權限ID',
  privilege_name varchar(50) comment '權限名稱',
  privilege_url varchar(200) comment '權限URL',
  primary key (id)
);
alter table sys_privilege comment '權限表';

create table sys_user_role (
  user_id bigint comment '用戶ID',
  role_id bigint comment '角色ID'
);
alter table sys_user_role comment '用戶角色關聯(lián)表';

create table sys_role_privilege (
  role_id bigint comment '角色ID',
  privilege_id bigint comment '權限ID'
);
alter table sys_role_privilege comment '角色權限關聯(lián)表';

測試數(shù)據(jù)

insert into `sys_user`
  values
    (1, 'admin', '123456', 'admin@mybatis', '管理員', null, now()),
    (1001, 'test', '123456', 'test@mybatis', '測試用戶', null, now());

insert into sys_role
    values
      (1, '管理員', '1', '1', now()),
      (2, '普通用戶', '1', '1', now());

insert into sys_user_role values (1, 1), (1, 2), (1001, 2);

insert sys_privilege
  values
    (1, '用戶管理', '/users'),
    (2, '角色管理', '/roles'),
    (3, '系統(tǒng)日志', '/logs'),
    (4, '人員維護', '/persons'),
    (5, '單位維護', '/companies');

insert sys_role_privilege
  values (1, 1), (1, 3), (1, 2), (2, 4), (2, 5);

對應的實體類根據(jù)數(shù)據(jù)庫的字段建立就好了菠赚。

關于每個表的單表操作,在前面一個小節(jié)已經(jīng)研究過了郑藏,所以在這個小節(jié)里衡查,就不演示單表的操作了。

多表操作必盖,本質(zhì)上其實就是連接多個表拌牲,然后查詢出數(shù)據(jù),根據(jù)關聯(lián)對象之間的關系歌粥,又可以分為1對1操作塌忽,1對多操作,多對多操作(本質(zhì)上而言其實也是1對多)失驶,所以接下來土居,我們分兩個部分來看如何通過MyBatis來操作

1對1操作

假設我們要根據(jù)用戶的ID查詢出用戶的角色,并且假定一個用戶只有一個角色(當然嬉探,實際上不止)擦耀,這里以1001號用戶為例,其在數(shù)據(jù)庫中也僅有一個角色涩堤,所以符合我們操作的要求眷蜓。

為了能通過MyBatis自動封裝,我們在SysUser中增加一個字段SysRole

public class SysUser {
    // 其他字段與數(shù)據(jù)庫保持一致即可
    private SysRole role;
    // set() get() toString()
}

在查詢操作中定躏,我們可以通過下面的方式來獲取數(shù)據(jù)

<select id="selectUserAndRoleById" resultType="domain.SysUser">
    select
        u.id,
        u.user_name userName,
        u.user_password userPassword,
        u.user_email userEmail,
        u.create_time createTime,
        <!--
            注意從這里開始的別名是"role.XXX"账磺,因為字段中是role
            為了能夠自動注入汉矿,所以需要采用obj.attr的形式捧书,
            如果有多級對象塘雳,則是 a.b.c這種形式
        -->
        r.id "role.id",
        r.role_name "role.roleName",
        r.enabled "role.enable",
        r.create_by "role.createBy",
        r.create_time "role.createTime"
    from sys_user u 
        join sys_user_role ur on u.id = ur.user_id
        join sys_role r on r.id = ur.role_id
    where u.id = #{id}
</select>

上面的實現(xiàn)方式從結果來看是沒有問題的,但是從工程的角度來講碧聪,其實不太好,尤其是當存在多個不同類型的查詢液茎,比如根據(jù)ID逞姿,根據(jù)名稱,根據(jù)郵箱地址等捆等,我們需要編寫多份的代碼滞造,并且其中的select部分基本上是不變的,也就是帶來非常明顯的冗余了栋烤。

更好地解決方案是使用MyBatis中的resultMap谒养,通過resultMap來封裝,可以實現(xiàn)代碼復用的目的

<resultMap id="userRoleMap" type="domain.SysUser">
    <id property="id" column="id"/>
    <!--其他的字段-->
    <result property="role.id" column="r.id"/>
    <!--其他的字段-->
</resultMap>

<select id="selectUserAndRoleById" resultMap="userRoleMap">
 ... 
 這里根據(jù)對應的字段調(diào)整一下明郭,只需要能正確映射就行
</select>

不過上面的內(nèi)容語義不明顯买窟,更好的方式是使用resutlMap<association>標簽來關聯(lián)對象丰泊,如下

<resultMap id="userRoleMap" type="domain.SysUser">
    <id property="id" column="id"/>
    <!--其他的字段-->

    <!--
        注意這里,使用的是association始绍,association的使用跟resultMap是類似的
        并且使用多了columnPrefix屬性瞳购,為了區(qū)分來自不同表的字段,
        如果是多級的嵌套亏推,則需要指定多級学赛,如 role_pri_XXX,一個層次的columnPrefix會
        去過濾每一次匹配的前綴
        當然径簿,在查詢的時候也需要將對應的前綴標注出來
    -->
    <association property="role" javaType="domain.SysRole" columnPrefix="role_">
        <result property="id" column="id"/>
        <!--其他的字段-->
    </association>
</resultMap>

<!--注意下面的內(nèi)容 role_也即是columnPrefix=""中指定的字段-->
<select>
    r.id role_id,
    r.role_name role_role_name,
    r.enabled  role_enabled,
    r.create_by role_create_by,
    r.create_time role_create_time
</select>

通過上面的方式罢屈,當需要的時候,就可以直接指定查詢的resultMap="userRoleMap"即可篇亭,已經(jīng)減少了一部分的重復操作了缠捌,但是,上面的方式仍然不是合適的译蒂,因為既然有user對應的map曼月,那實際上將role對應的字段也封裝到map中,然后直接調(diào)用即可柔昼,這樣哑芹,多個使用到role的地方都可以直接使用了

首先在SysRoleMapper.xml定義對應的roleMap,當然捕透,放在其他的mapper里也是可以聪姿,但是放在SysRoleMapper.xml是最合適的

<mapper namespace="mapper.SysRoleMapper">
    <resultMap id="roleMapper" type="domain.SysRole">
        <id property="id" column="id"/>
        <!--其他的字段-->
    </resultMap>
</mapper>

整理完之后的userRoleMap內(nèi)容如下

<resultMap id="userRoleMap" type="domain.SysUser">
    <id property="id" column="id"/>
    <!--其他的字段-->
    <!--這里使用resultMap來指定其他的resultMap,如果不在本文件乙嘀,則使用全限定名-->
    <association property="role" columnPrefix="role_" resultMap="mapper.SysRoleMapper.roleMapper"/>
</resultMap>

經(jīng)過上面的整理之后末购,現(xiàn)在的整體結構就變得非常靈活了,特別是當我們需要組合多個對象的時候虎谢,通過這種方式盟榴,可以實現(xiàn)只需要定義一個resultMap,然后在多處使用

1對多操作

有了上面封裝1對1的操作過程作為基礎婴噩,實現(xiàn)一對多就容易很多了擎场,只需要將<association>替換為<collection>即可,當然几莽,由于上面為了方便迅办,直接在SysUser中定義了一個SysRole對象,但實際上我們知道章蚣,一個用戶是可以對應多個角色的站欺,所以,在SysUser中應該定義的是一個SysRole容器,比如list或者set等镊绪,也就是實際上1對多的操作啦

<resultMap id="userRoleMap" type="domain.SysUser">
    <!--注意這里-->
    <collection property="role" columnPrefix="role_" resultMap="mapper.SysRoleMapper.roleMapper"/>
</resultMap>

可以看到匀伏,因為為role對象定義roleMap,所以蝴韭,當改動userRole時够颠,其他的內(nèi)容完全不需要改動

一個完整的例子

在上面的兩步操作中,我們已經(jīng)充分體驗到了MyBatis中的resultMap榄鉴、assocation以及collection提供的便利履磨,下面我們通過完整的例子,來加深對其認識

這里通過用戶ID庆尘,獲取其所有的角色以及所有角色對應的權限

將對應的實體類調(diào)整為如下

SysUser

public class SysUser {
    // 一個用戶可能對應多個角色
    private List<SysRole> role;
}

SysRole

public class SysRole {
    // 一個角色可能有多個權限
    private List<SysPrivilege> privilegeList;
}

然后為每個實體類編寫對應的resultMap剃诅,這個參考上面的編寫方式就行啦,這里就不貼代碼了

接下來組合多個resultMap驶忌,這里我們采用自底向上的方式

<resultMap id="rolePrivilegeMap" type="domain.SysRole">
    <id property="id" column="id"/>
    <result property="createBy" column="create_by"/>
    <result property="createTime" column="create_time"/>
    <result property="roleName" column="role_name"/>
    <result property="enabled" column="enabled"/>
    <!--組裝privilegeMap-->
    <collection property="privilegeList" columnPrefix="pri_" resultMap="mapper.SysUserMapper.privilegeMap"/>
</resultMap>
<resultMap id="userRoleMap" type="domain.SysUser">
    <id property="id" column="id"/>
    <result property="userName" column="user_name"/>
    <result property="userPassword" column="user_password"/>
    <result property="userEmail" column="user_email"/>
    <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
    <!--組裝rolePrivilegeMap-->
    <collection property="role" columnPrefix="role_" resultMap="mapper.SysRoleMapper.rolePrivilegeMap"/>
</resultMap>

通過上面的兩層組裝之后矛辕,當我們需要使用的時候,就可以直接指定resultMap="userRoleMap"即可啦

關于ResultMap付魔,還有一個小點需要注意聊品,如果查詢的數(shù)據(jù)中不包含某些字段,而resultMap中有該字段時几苍,MyBatis會忽略該字段翻屈,所以,一個resultMap可以復用在其他場景妻坝,即使查詢的字段跟resultMap中的字段不完全匹配伸眶,只要resultMap中包含我們需要的字段即可

discriminator

在ResultMap中,還有一個<discriminator>刽宪,該標簽的用途在于厘贼,根據(jù)不同的字段值進行分類,比如在上面的案例中纠屋,有一些角色是啟用的涂臣,有一些是不允許啟用的盾计,那么售担,對于不允許啟用的角色,我們就不需要獲取其角色以及權限信息署辉,所以族铆,這時,可以通過discriminator來實現(xiàn)根據(jù)不同的值來映射到不同的resutMap中哭尝,如下面所示

<resultMap id="userMap" type="domain.SysUser">
    <id property="id" column="id"/>
    <result property="userName" column="user_name"/>
    <result property="userPassword" column="user_password"/>
    <result property="userEmail" column="user_email"/>
    <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
</resultMap>

<resultMap id="userRoleMap" type="domain.SysUser">
    <!--根據(jù)role_enabled的狀態(tài)來選擇不同的查詢-->
    <discriminator javaType="int" column="role_enabled">
        <case value="1" resultMap="userRoleMapSelect" />
        <!--只獲取用戶的基本信息哥攘,不獲取角色以及權限信息-->
        <case value="0" resultMap="userMap"/>
    </discriminator>
</resultMap>

<!--直接繼承userMap,可以避免編寫過多的result標簽-->
<resultMap id="userRoleMapSelect" type="domain.SysUser" extends="userMap">
    <collection property="role" columnPrefix="role_" resultMap="mapper.SysRoleMapper.rolePrivilegeMap"/>
</resultMap>

通過上面的例子,可以看到discriminator的強大之處了逝淹,在使用discriminator的時候需要注意耕姊,discriminator是作用在當前的resultMap的,也就是說栅葡,discriminator中的resultMap封裝的是當前的result中的內(nèi)容茉兰,而不是決定子查詢中的內(nèi)容

總結

本小節(jié)主要學習了MyBatis中的多表查詢,通過MyBatis中的resultMap以及resultMap中的association欣簇、collection规脸,可以實現(xiàn)一對一,一對多查詢中結果的自動封裝熊咽,而通過discriminator則可以根據(jù)不同的數(shù)值來選擇返回不同的resultMap莫鸭,通過resultMap中的extends屬性,可以復用一個已經(jīng)存在的resultMap横殴,通過多個resultMap的復用被因,可以極大地提高代碼的復用率,使得代碼更加簡潔衫仑。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末氏身,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子惑畴,更是在濱河造成了極大的恐慌蛋欣,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件如贷,死亡現(xiàn)場離奇詭異陷虎,居然都是意外死亡,警方通過查閱死者的電腦和手機杠袱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門尚猿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人楣富,你說我怎么就攤上這事凿掂。” “怎么了纹蝴?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵庄萎,是天一觀的道長。 經(jīng)常有香客問我塘安,道長糠涛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任兼犯,我火速辦了婚禮集漾,結果婚禮上,老公的妹妹穿的比我還像新娘砸脊。我一直安慰自己具篇,他們只是感情好,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布凌埂。 她就那樣靜靜地躺著栽连,像睡著了一般。 火紅的嫁衣襯著肌膚如雪侨舆。 梳的紋絲不亂的頭發(fā)上秒紧,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天,我揣著相機與錄音挨下,去河邊找鬼熔恢。 笑死,一個胖子當著我的面吹牛臭笆,可吹牛的內(nèi)容都是我干的叙淌。 我是一名探鬼主播,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼愁铺,長吁一口氣:“原來是場噩夢啊……” “哼鹰霍!你這毒婦竟也來了?” 一聲冷哼從身側響起茵乱,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤茂洒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后瓶竭,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體督勺,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年斤贰,在試婚紗的時候發(fā)現(xiàn)自己被綠了智哀。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡荧恍,死狀恐怖瓷叫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情送巡,我是刑警寧澤摹菠,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站授艰,受9級特大地震影響辨嗽,放射性物質(zhì)發(fā)生泄漏世落。R本人自食惡果不足惜淮腾,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一糟需、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧谷朝,春花似錦洲押、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至专钉,卻和暖如春挑童,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背跃须。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工站叼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人菇民。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓尽楔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親第练。 傳聞我的和親對象是個殘疾皇子阔馋,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350

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

  • 1. 簡介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL娇掏、存儲過程以及高級映射的優(yōu)秀的...
    笨鳥慢飛閱讀 5,461評論 0 4
  • MyBatis 理論篇 [TOC] 什么是MyBatis ?MyBatis是支持普通SQL查詢,存儲過程和高級映射...
    有_味閱讀 2,889評論 0 26
  • 早已經(jīng)習慣一個人想就這么簡單的生活到老卻在不經(jīng)意間與你遇到于是又有了祈禱想要那一生一次一次一生的擁抱 早已經(jīng)不相信...
    justwind閱讀 185評論 0 1
  • 在去年的時候就有在微博見過一個博主推薦過這本書呕寝,光看書名真是沒有覺得這是一本時間管理書。 但是這本書確定就是時間管...
    鄭珍容閱讀 451評論 0 1