千字文帶你入門-MyBatis多表查詢

image

MyBatis多表操作

經(jīng)過了 MyBatis 基本增刪改查的學習商膊,而在實際的項目中宾濒,我們往往會接觸到多表的操作,什么是多表呢仅淑, 在實際生活中称勋,每個實體之間往往是存在關系的,而我們的項目卻是要依賴數(shù)據(jù)庫將這些實體之間的關系串聯(lián)起來涯竟,從而實現(xiàn)我們的業(yè)務赡鲜,所以這部分空厌,我們著重講解如何使用 MyBatis 框架處理多張數(shù)據(jù)表之間的聯(lián)系,幫助我們更加理解數(shù)據(jù)庫的映射關系

(一) 表間關系

A:一對多

  • 用戶和訂單/理財產(chǎn)品

    • 一個用戶可以買好幾個批次的理財產(chǎn)品
  • 部門和員工

    • 一個部門可以有很多員工

B:多對一

  • 訂單和用戶
    • 多個訂單屬于同一個用戶

C:多對多

  • 學生選課和學生
    • 一個學生可以選擇多門課银酬,一門課可以被多個學生選擇

D:一對一

  • 身份證蝇庭、護照等證件
    • 一個證件只能屬于一個人

可以看到,第二章內(nèi)容我們直接進入了業(yè)務表的內(nèi)容捡硅,而由于前幾篇文章的鋪墊哮内,我將User的相關信息都沒有講解,缺失的內(nèi)容只有用戶實體類壮韭,以及對應 XML 映射文件北发,這個非常簡單 以及對應測試類

(二) 根據(jù)業(yè)務創(chuàng)建表

文章中我們使用用戶和賬戶之間的賬戶的關系,即:

  • 一個用戶可以擁有多個賬戶喷屋,一個賬戶只能屬于一個用戶琳拨,多個賬戶也可以屬于同一個用戶

首先需要建立兩張表:用戶表和賬戶表

  • 讓兩者分別具備一對多的關系,我們需要在賬戶表中添加外鍵

User表

CREATE TABLE USER (
 `id`           INT(11)NOT NULL AUTO_INCREMENT,
 `username`     VARCHAR(32) NOT NULL COMMENT '用戶名',
 `telephone`    VARCHAR(11) NOT NULL COMMENT '手機',
 `birthday`     DATETIME DEFAULT NULL COMMENT '生日',
 `gender`       CHAR(1) DEFAULT NULL COMMENT '性別',
 `address`      VARCHAR(256) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY  (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

Account表

CREATE TABLE `account` (
  `ID` int(11) NOT NULL COMMENT '編號',
  `UID` int(11) default NULL COMMENT '用戶編號',
  `MONEY` double default NULL COMMENT '金額',
  PRIMARY KEY  (`ID`),
  KEY `FK_Reference_8` (`UID`),
  CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

(三) 賬戶表-單表查詢

首先創(chuàng)建其對應Account實體類

public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private Double money;
    ......對應 get set 方法
}

在其 AccountMappre 接口中增加查詢所有的方法

public interface AccountMapper {

    /**
     * 查詢所有賬戶
     * @return
     */
    List<Account> findAll();
}

增加其映射文件屯曹,注:省略頭部的一些引入代碼

<mapper namespace="cn.ideal.mapper.AccountMapper">
    <!-- 根據(jù)查詢所有用戶 -->
    <select id="findAll" resultType="Account">
        select * from account
    </select>
</mapper>

還是要再多啰嗦一句狱庇,resultType="Account" 這里是因為我們在主配置文件中已經(jīng),對omain中類都起了別名恶耽,所以可以直接用包下的類名密任,如果不了解的朋友,使用全類名也是一樣的

測試一下:

/**
 * 測試查詢所有
 */
@Test
public void testFindAll(){
    List<Account> accounts = accountMapper.findAll();
    for (Account account : accounts){
        System.out.println(account);
    }
}

看一下效果:

image

(四) Account 一對一查詢

如何查詢到 Acount 中信息的同時偷俭,根據(jù)用戶的 id 值將對應的數(shù)據(jù)顯示出來浪讳,這其實主要就是需要改變 SQL 的寫法,我們在本地的 MySQL中先試一試

SELECT FROM account a,user u WHERE u.id=a.uid;

執(zhí)行結(jié)果

image

結(jié)果出來了涌萤,但是 user 表中的 id 屬性由于和 account 表中的 id 屬性名稱是一致的淹遵,所以自動起了別名,更好的做法是负溪,我們自己設置其對應的別名

SELECT u.*,a.id as aid,a.uid,a.money FROM account a,user u WHERE u.id=a.uid;
image

這樣看起來就條理了許多

到了這一步透揣,我們就可以在代碼中實現(xiàn)這樣的功能了,即通過查詢賬戶信息川抡,同時查詢出對應的用戶信息辐真,那由于注冊時間,男女等信息猖腕,我并不想要拆祈,怎么辦呢?我們可以再加一點約束倘感,用戶的信息只顯示名稱和地址兩個字段

A:創(chuàng)建子類方式(不算太常用)

(1) 修改 Account 接口

/**
 * 查詢所有賬戶放坏,并且?guī)в杏脩裘Q和地址信息
 * @return
 */
List<UserAccount> findAllAccount();

大家可能注意到我們返回的 List 類型為 UserAccount,這是為什么呢老玛?

既然我們想返回的信息中淤年,需要包含兩個表中的信息钧敞,似乎我們并沒有一個實體可以承載這么多信息,所以我們創(chuàng)建一個 UserAccount 類

(2) 創(chuàng)建 UserAccount 類

public class UserAccount extends Account {
    private String username;
    private String address;
    ......對應 get set 方法
    
    @Override
    public String toString() {
        return super.toString() + "  UserAccount{" +
                "username='" + username + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

說明:由于我們只需要顯示 名稱 和 地址 這兩個字段麸粮,所以只需要創(chuàng)建 username 和 address 兩個字段就可以了溉苛,而繼承 Account 可以方便我們調(diào)用輸出查詢到賬戶中的信息

(3) 修改 AccountMapper.xml

<select id="findAllAccount" resultType="UserAccount">
        select a.*,u.username,u.address from account a , user u where u.id = a.uid;
    </select>

(4) 測試代碼

/**
 * 查詢所有賬戶,并且?guī)в杏脩裘Q和地址信息
 * @return
 */
@Test
public void testFindAllAccount(){
    List<UserAccount> uas = accountMapper.findAllAccount();
    for (UserAccount ua : uas ){
        System.out.println(ua);
    }
}

(5) 執(zhí)行效果

image

B:建立實體類關系方式(推薦)

(1) 修改 Account 接口

/**
 * 查詢所有賬戶
 * @return
 */
List<Account> findAll();

(2) 修改 Account 類

在 Account 類中需要增加一個User對象的引用弄诲,這也就是對應著我們的 user 主表

//從表實體應該包含一個主表實體的對象引用
private User user;

public User getUser() {
    return user;
}

public void setUser(User user) {
    this.user = user;
}

(3) 修改 AccountMapper.xml

<!-- 定義封裝 Account和User 的resultMap -->
<resultMap id="userAccountMap" type="Account">
    <id property="id" column="aid"></id>
    <result property="uid" column="uid"></result>
    <result property="money" column="money"></result>
    <!-- 配置封裝 User 的內(nèi)容 -->
    <association property="user" column="uid" javaType="User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="telephone" column="telephone"></result>
        <result property="birthday" column="birthday"></result>
        <result property="gender" column="gender"></result>
        <result property="address" column="address"></result>
     </association>
</resultMap>

<!-- 根據(jù)查詢所有用戶 -->
 <select id="findAll" resultMap="userAccountMap">
    SELECT u.*,a.id AS aid,a.uid,a.money FROM account a,user u WHERE u.id = a.uid;
</select>

說明:由于我們想要返回的結(jié)果為多個值愚战,是沒有一個完全與返回結(jié)果值一一匹配的封裝類去接收的,所以我們可以使用MyBatis 提供的 resultMap 進行接收結(jié)果數(shù)據(jù)齐遵,它會在列名和 Java 包裝類屬性名之間創(chuàng)建映射關系寂玲,這篇文章的重心還是放在表的操作上,關于這個問題梗摇,以后可以專門寫篇文章進行說明拓哟,如果對這部分不清楚的朋友,可以自己查一下這些標簽的意義伶授,實際上不會太過于復雜的

(4) 測試代碼

/**
 * 測試查詢所有
 */
@Test
public void testFindAll(){
    List<Account> accounts = accountMapper.findAll();
    for (Account account : accounts){
        System.out.println("--------------------------");
        System.out.println(account);
        System.out.println(account.getUser());
    }
}

(5) 執(zhí)行效果

image

(五) User 一對多查詢

(1) 修改 UserMapper 接口

/**
 * 查詢所有用戶信息,同時顯示出該用戶下的所有賬戶
 *
 * @return
 */
List<User> findAll();

(2) 修改 User 類

在 Java 類中應該添加一個集合成員断序,類型為 Account,方便我們承載賬戶的信息

//一對多關系映射糜烹,主表實體應該包含從表實體的集合引用
private List<Account> accounts;

public List<Account> getAccounts() {
    return accounts;
}

public void setAccounts(List<Account> accounts) {
    this.accounts = accounts;
}

(3) 修改 AccountMapper.xml

<!-- 定義User的resultMap-->
<resultMap id="userAccountMap" type="User">
    <id property="id" column="id"></id>
    <result property="username" column="username"></result>
    <result property="telephone" column="telephone"></result>
    <result property="birthday" column="birthday"></result>
    <result property="gender" column="gender"></result>
    <result property="address" column="address"></result>
    <collection property="accounts" ofType="account">
        <id property="id" column="aid"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
    </collection>
</resultMap>

<!-- 根據(jù)查詢所有用戶 -->
<select id="findAll" resultMap="userAccountMap">
   SELECT * FROM user u LEFT OUTER JOIN account a on u.id = a.uid;
</select>

注:LEFT OUTER JOIN :左外連接违诗,可以將左表的數(shù)據(jù)全部顯示出來

(4) 測試代碼

/**
 * 測試查詢所有
 */
@Test
public void testFindAll() {
    List<User> users= userMapper.findAll();
    for (User user : users) {
        System.out.println("--------------------------");
        System.out.println(user);
        System.out.println(user.getAccounts());
    }
}

(5) 執(zhí)行效果

image

可以看到,所有用戶信息被打印了出來(上圖只截取了前面的部分)景图,并且在用戶下存在所有賬戶的信息也被打印了出來

(六) 多對多操作

前面我們看完了较雕,用戶以及賬戶之間一對多的關系,下面我們來研究一下多對多的情況挚币,這種,情況會麻煩一些扣典,例如我們舉個例子:用戶以及職位之間的關系

  • 一個用戶可以有多個職位妆毕,而一個職位也可以屬于多個用戶

但是如何將兩個表連接起來呢?這就需要一個中間表贮尖,用來使得兩個表之間產(chǎn)生關系

首先創(chuàng)建一個職位表

CREATE TABLE `role` (
  `ID` int(11) NOT NULL COMMENT '編號',
  `ROLE_NAME` varchar(30) default NULL COMMENT '職位',
  `ROLE_DESC` varchar(60) default NULL COMMENT '描述',
  PRIMARY KEY  (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert  into `role`(`ID`,`ROLE_NAME`,`ROLE_DESC`) values (1,'班主任','管理整個班'),(2,'院長','管理整個學院'),(3,'校長','管理整個學校');

接著我們創(chuàng)建中間表

CREATE TABLE `user_role` (
  `UID` int(11) NOT NULL COMMENT '用戶編號',
  `RID` int(11) NOT NULL COMMENT '職位編號',
  PRIMARY KEY  (`UID`,`RID`),
  KEY `FK_Reference_10` (`RID`),
  CONSTRAINT `FK_Reference_10` FOREIGN KEY (`RID`) REFERENCES `role` (`ID`),
  CONSTRAINT `FK_Reference_9` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


insert  into `user_role`(`UID`,`RID`) values (12,1),(16,1),(12,2);

至于用戶表笛粘,我們依舊沿用前面的 user 的一套

A:查詢所有職位信息

(1) 創(chuàng)建實體

public class Role implements Serializable {
    private Integer roleId;
    private String roleName;
    private String roleDesc;
    ...... 省略對應 get set toString 方法
}

(2) 創(chuàng)建接口并增加方法

public interface RoleMapper {
    /**
     * 查詢所有職位
     * @return
     */
    List<Role> findAll();
}

(3) 創(chuàng)建 RoleMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.ideal.mapper.RoleMapper">

    <!-- 定義Role的resultMap-->
    <resultMap id="roleMap" type="Role">
        <id property="roleId" column="id"></id>
        <result property="roleName" column="role_name"></result>
        <result property="roleDesc" column="role_desc"></result>
    </resultMap>

    <!-- 根據(jù)查詢所有用戶 -->
    <select id="findAll" resultMap="roleMap">
       SELECT * FROM role
    </select>

</mapper>

需要特別注意的是:column中的值是數(shù)據(jù)庫中字段名,而property中的值是JavaBean中的對應成員變量湿硝,由于兩者的名字并不是相同的薪前,所以請注意區(qū)分

(4) 測試代碼

@Test
public void testFindAll(){
    List<Role> roles = roleMapper.findAll();
    for (Role role : roles){
        System.out.println("-----------------------");
        System.out.println(role);
     }
}

(5) 執(zhí)行效果

image

B:查詢角色獲取對應職位

(1) 修改 Role 類

在 Role 實體類中增加 User 類型的 List集合

//多對多關系映射,一個職位可以擁有多個用戶
private List<User> users;

public List<User> getUsers() {
    return users;
}

public void setUsers(List<User> users) {
    this.users = users;
}

接口方法我們?nèi)匀挥们懊鎰?chuàng)建的关斜,findAll 方法

(2) 修改 RoleMapper.xml

在這部分示括,毫無疑問,需要創(chuàng)建 Role 的 resultMap 痢畜,還要一部分垛膝,就是對應 SQL 語句的編寫

SQL語句的編寫我們需要簡單的分析一下鳍侣,首先看一下,三張表之間的關系

image

中間表通過UID RID兩個字段分別連接起了 user 和 role 兩張表

先通過 role 表中的 id 找到 中間表的 rid 然后通過 rid 對應的 uid值 找到 user 表中的 id 值吼拥,從而獲取到對應的用戶信息

這個時候我們需要兩個左外連接倚聚,xml 代碼如下

<mapper namespace="cn.ideal.mapper.RoleMapper">
    <!-- 定義Role的resultMap-->
    <resultMap id="roleMap" type="Role">
        <id property="roleId" column="rid"></id>
        <result property="roleName" column="role_name"></result>
        <result property="roleDesc" column="role_desc"></result>
        <collection property="users" ofType="User">
            <id property="id" column="id"></id>
            <result property="username" column="username"></result>
            <result property="telephone" column="telephone"></result>
            <result property="birthday" column="birthday"></result>
            <result property="gender" column="gender"></result>
            <result property="address" column="address"></result>
        </collection>
    </resultMap>

    <!-- 根據(jù)查詢所有用戶 -->
    <select id="findAll" resultMap="roleMap">
       SELECT u.*,r.id AS rid,r.role_name,r.role_desc FROM role r
       LEFT OUTER JOIN user_role ur ON r.id = ur.rid
       LEFT OUTER JOIN user u ON u.id = ur.uid
    </select>
</mapper>

(3) 測試代碼

@Test
public void testFindAll(){
    List<Role> roles = roleMapper.findAll();
    for (Role role : roles){
        System.out.println("---------------------");
        System.out.println(role);
        System.out.println(role.getUsers());
    }
}

(4) 執(zhí)行效果

image

C:查詢職位獲取對應用戶

(1) 修改接口方法

public interface UserMapper {
    /**
     * 查詢所有用戶信息,同時顯示出該用戶下的所有賬戶
     *
     * @return
     */
    List<User> findAll();

}

(2) 修改 User 實體

這是多對多的關系映射,一個用戶可以具備多個角色

private List<Role> roles;

public List<Role> getRoles() {
    return roles;
}

public void setRoles(List<Role> roles) {
    this.roles = roles;
}

(3) 修改 RoleMapper.xml

<mapper namespace="cn.ideal.mapper.RoleMapper">
    <!-- 定義Role的resultMap-->
    <resultMap id="roleMap" type="Role">
        <id property="roleId" column="rid"></id>
        <result property="roleName" column="role_name"></result>
        <result property="roleDesc" column="role_desc"></result>
        <collection property="users" ofType="User">
            <id property="id" column="id"></id>
            <result property="username" column="username"></result>
            <result property="telephone" column="telephone"></result>
            <result property="birthday" column="birthday"></result>
            <result property="gender" column="gender"></result>
            <result property="address" column="address"></result>
        </collection>
    </resultMap>

    <!-- 根據(jù)查詢所有用戶 -->
    <select id="findAll" resultMap="roleMap">
       SELECT u.*,r.id AS rid,r.role_name,r.role_desc FROM role r
       LEFT OUTER JOIN user_role ur ON r.id = ur.rid
       LEFT OUTER JOIN user u ON u.id = ur.uid
    </select>
</mapper>

(4) 測試代碼

@Test
public void testFindAll(){
    List<User> users = userMapper.findAll();
    for (User user : users){
        System.out.println("---------------------");
        System.out.println(user);
        System.out.println(user.getRoles());
    }
}

(5) 執(zhí)行效果

image

結(jié)尾

如果文章中有什么不足凿可,歡迎大家留言交流惑折,感謝朋友們的支持!

如果能幫到你的話枯跑,那就來關注我吧唬复!如果您更喜歡微信文章的閱讀方式,可以關注我的公眾號

在這里的我們素不相識全肮,卻都在為了自己的夢而努力 ?

一個堅持推送原創(chuàng)開發(fā)技術文章的公眾號:理想二旬不止

image
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末敞咧,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子辜腺,更是在濱河造成了極大的恐慌休建,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件评疗,死亡現(xiàn)場離奇詭異测砂,居然都是意外死亡,警方通過查閱死者的電腦和手機百匆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門砌些,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人加匈,你說我怎么就攤上這事存璃。” “怎么了雕拼?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵纵东,是天一觀的道長。 經(jīng)常有香客問我啥寇,道長偎球,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任辑甜,我火速辦了婚禮衰絮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘磷醋。我一直安慰自己猫牡,他們只是感情好,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布子檀。 她就那樣靜靜地躺著镊掖,像睡著了一般乃戈。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上亩进,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天症虑,我揣著相機與錄音,去河邊找鬼归薛。 笑死谍憔,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的主籍。 我是一名探鬼主播习贫,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼千元!你這毒婦竟也來了苫昌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤幸海,失蹤者是張志新(化名)和其女友劉穎祟身,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體物独,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡袜硫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了挡篓。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片婉陷。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖官研,靈堂內(nèi)的尸體忽然破棺而出秽澳,到底是詐尸還是另有隱情,我是刑警寧澤阀参,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布肝集,位于F島的核電站,受9級特大地震影響蛛壳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜所刀,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一衙荐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧浮创,春花似錦忧吟、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽讹俊。三九已至,卻和暖如春煌抒,著一層夾襖步出監(jiān)牢的瞬間仍劈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工寡壮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贩疙,地道東北人。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓况既,卻偏偏與公主長得像这溅,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子棒仍,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

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