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);
}
}
看一下效果:
(四) Account 一對一查詢
如何查詢到 Acount 中信息的同時偷俭,根據(jù)用戶的 id 值將對應的數(shù)據(jù)顯示出來浪讳,這其實主要就是需要改變 SQL 的寫法,我們在本地的 MySQL中先試一試
SELECT FROM account a,user u WHERE u.id=a.uid;
執(zhí)行結(jié)果
結(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;
這樣看起來就條理了許多
到了這一步透揣,我們就可以在代碼中實現(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í)行效果
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í)行效果
(五) 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í)行效果
可以看到,所有用戶信息被打印了出來(上圖只截取了前面的部分)景图,并且在用戶下存在所有賬戶的信息也被打印了出來
(六) 多對多操作
前面我們看完了较雕,用戶以及賬戶之間一對多的關系,下面我們來研究一下多對多的情況挚币,這種,情況會麻煩一些扣典,例如我們舉個例子:用戶以及職位之間的關系
- 一個用戶可以有多個職位妆毕,而一個職位也可以屬于多個用戶
但是如何將兩個表連接起來呢?這就需要一個中間表贮尖,用來使得兩個表之間產(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í)行效果
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語句的編寫我們需要簡單的分析一下鳍侣,首先看一下,三張表之間的關系
中間表通過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í)行效果
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í)行效果
結(jié)尾
如果文章中有什么不足凿可,歡迎大家留言交流惑折,感謝朋友們的支持!
如果能幫到你的話枯跑,那就來關注我吧唬复!如果您更喜歡微信文章的閱讀方式,可以關注我的公眾號
在這里的我們素不相識全肮,卻都在為了自己的夢而努力 ?
一個堅持推送原創(chuàng)開發(fā)技術文章的公眾號:理想二旬不止