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

MyBatis多表操做

通過了 MyBatis 基本增刪改查的學習,而在實際的項目中,咱們每每會接觸到多表的操做,什麼是多表呢, 在實際生活中,每一個實體之間每每是存在關係的,而咱們的項目倒是要依賴數據庫將這些實體之間的關係串聯起來,從而實現咱們的業務,因此這部分,咱們着重講解如何使用 MyBatis 框架處理多張數據表之間的聯繫,幫助咱們更加理解數據庫的映射關係java

(一) 表間關係

A:一對多

  • 用戶和訂單/理財產品sql

    • 一個用戶能夠買好幾個批次的理財產品
  • 部門和員工數據庫

    • 一個部門能夠有不少員工

B:多對一

  • 訂單和用戶微信

    • 多個訂單屬於同一個用戶

C:多對多

  • 學生選課和學生mybatis

    • 一個學生能夠選擇多門課,一門課能夠被多個學生選擇

D:一對一

  • 身份證、護照等證件app

    • 一個證件只能屬於一我的

能夠看到,第二章內容咱們直接進入了業務表的內容,而因爲前幾篇文章的鋪墊,我將User的相關信息都沒有講解,缺失的內容只有用戶實體類,以及對應 XML 映射文件,這個很是簡單 以及對應測試類框架

(二) 根據業務建立表

文章中咱們使用用戶和帳戶之間的帳戶的關係,即:ide

  • 一個用戶能夠擁有多個帳戶,一個帳戶只能屬於一個用戶,多個帳戶也能夠屬於同一個用戶

首先須要創建兩張表:用戶表和帳戶表學習

  • 讓二者分別具有一對多的關係,咱們須要在帳戶表中添加外鍵

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;

(三) 帳戶表-單表查詢

首先建立其對應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">
    <!-- 根據查詢全部用戶 -->
    <select id="findAll" resultType="Account">
        select * from account
    </select>
</mapper>

仍是要再多囉嗦一句,resultType="Account" 這裏是由於咱們在主配置文件中已經,對omain中類都起了別名,因此能夠直接用包下的類名,若是不瞭解的朋友,使用全類名也是同樣的

測試一下:

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

看一下效果:

(四) Account 一對一查詢

如何查詢到 Acount 中信息的同時,根據用戶的 id 值將對應的數據顯示出來,這其實主要就是須要改變 SQL 的寫法,咱們在本地的 MySQL中先試一試

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

執行結果

結果出來了,可是 user 表中的 id 屬性因爲和 account 表中的 id 屬性名稱是一致的,因此自動起了別名,更好的作法是,咱們本身設置其對應的別名

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

這樣看起來就條理了許多

到了這一步,咱們就能夠在代碼中實現這樣的功能了,即經過查詢帳戶信息,同時查詢出對應的用戶信息,那因爲註冊時間,男女等信息,我並不想要,怎麼辦呢?咱們能夠再加一點約束,用戶的信息只顯示名稱和地址兩個字段

A:建立子類方式(不算太經常使用)

(1) 修改 Account 接口

/**
 * 查詢全部帳戶,而且帶有用戶名稱和地址信息
 * @return
 */
List<UserAccount> findAllAccount();

你們可能注意到咱們返回的 List 類型爲 UserAccount,這是爲何呢?

既然咱們想返回的信息中,須要包含兩個表中的信息,彷佛咱們並無一個實體能夠承載這麼多信息,因此咱們建立一個 UserAccount 類

(2) 建立 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 + '\'' +
                '}';
    }
}

說明:因爲咱們只須要顯示 名稱 和 地址 這兩個字段,因此只須要建立 username 和 address 兩個字段就能夠了,而繼承 Account 能夠方便咱們調用輸出查詢到帳戶中的信息

(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) 測試代碼

/**
 * 查詢全部帳戶,而且帶有用戶名稱和地址信息
 * @return
 */
@Test
public void testFindAllAccount(){
    List<UserAccount> uas = accountMapper.findAllAccount();
    for (UserAccount ua : uas ){
        System.out.println(ua);
    }
}

(5) 執行效果

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 的內容 -->
    <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>

<!-- 根據查詢全部用戶 -->
 <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>

說明:因爲咱們想要返回的結果爲多個值,是沒有一個徹底與返回結果值一一匹配的封裝類去接收的,因此咱們可使用MyBatis 提供的 resultMap 進行接收結果數據,它會在列名和 Java 包裝類屬性名之間建立映射關係,這篇文章的重心仍是放在表的操做上,關於這個問題,之後能夠專門寫篇文章進行說明,若是對這部分不清楚的朋友,能夠本身查一下這些標籤的意義,實際上不會太過於複雜的

(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) 執行效果

(五) 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>

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

注:LEFT OUTER JOIN :左外鏈接,能夠將左表的數據所有顯示出來

(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) 執行效果

能夠看到,全部用戶信息被打印了出來(上圖只截取了前面的部分),而且在用戶下存在全部帳戶的信息也被打印了出來

(六) 多對多操做

前面咱們看完了,用戶以及帳戶之間一對多的關係,下面咱們來研究一下多對多的狀況,這種,狀況會麻煩一些,例如咱們舉個例子:用戶以及職位之間的關係

  • 一個用戶能夠有多個職位,而一個職位也能夠屬於多個用戶

可是如何將兩個錶鏈接起來呢?這就須要一箇中間表,用來使得兩個表之間產生關係

首先建立一個職位表

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,'校長','管理整個學校');

接着咱們建立中間表

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) 建立實體

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

(2) 建立接口並增長方法

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

(3) 建立 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>

    <!-- 根據查詢全部用戶 -->
    <select id="findAll" resultMap="roleMap">
       SELECT * FROM role
    </select>

</mapper>

須要特別注意的是:column中的值是數據庫中字段名,而property中的值是JavaBean中的對應成員變量,因爲二者的名字並非相同的,因此請注意區分

(4) 測試代碼

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

(5) 執行效果

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;
}

接口方法咱們仍然用前面建立的,findAll 方法

(2) 修改 RoleMapper.xml

在這部分,毫無疑問,須要建立 Role 的 resultMap ,還要一部分,就是對應 SQL 語句的編寫

SQL語句的編寫咱們須要簡單的分析一下,首先看一下,三張表之間的關係

中間表經過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>

    <!-- 根據查詢全部用戶 -->
    <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) 執行效果

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>

    <!-- 根據查詢全部用戶 -->
    <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) 執行效果

結尾

若是文章中有什麼不足,歡迎你們留言交流,感謝朋友們的支持!

若是能幫到你的話,那就來關注我吧!若是您更喜歡微信文章的閱讀方式,能夠關注個人公衆號

在這裏的咱們素不相識,卻都在爲了本身的夢而努力 ❤

一個堅持推送原創開發技術文章的公衆號:理想二旬不止

相關文章
相關標籤/搜索