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

MyBatis多表操作

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

(一) 表間關係

A:一對多

  • 用戶和訂單/理財產品

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

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

B:多對一

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

C:多對多

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

D:一對一

  • 身份證、護照等證件
    • 一個證件只能屬於一個人

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

(二) 根據業務創建表

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

  • 一個用戶可以擁有多個賬戶,一個賬戶只能屬於一個用戶,多個賬戶也可以屬於同一個用戶

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

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

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

結尾

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

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

在這裏的我們素不相識,卻都在爲了自己的夢而努力 ❤

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章