千字文带你入门-MyBatis多表查询
文章图片
image MyBatis多表操作
经过了 MyBatis 基本增删改查的学习,而在实际的项目中,我们往往会接触到多表的操作,什么是多表呢, 在实际生活中,每个实体之间往往是存在关系的,而我们的项目却是要依赖数据库将这些实体之间的关系串联起来,从而实现我们的业务,所以这部分,我们着重讲解如何使用 MyBatis 框架处理多张数据表之间的联系,帮助我们更加理解数据库的映射关系
(一) 表间关系
A:一对多
- 用户和订单/理财产品
- 一个用户可以买好几个批次的理财产品
- 部门和员工
- 一个部门可以有很多员工
- 订单和用户
- 多个订单属于同一个用户
- 学生选课和学生
- 一个学生可以选择多门课,一门课可以被多个学生选择
- 身份证、护照等证件
- 一个证件只能属于一个人
(二) 根据业务创建表 文章中我们使用用户和账户之间的账户的关系,即:
- 一个用户可以拥有多个账户,一个账户只能属于一个用户,多个账户也可以属于同一个用户
- 让两者分别具备一对多的关系,我们需要在账户表中添加外键
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 findAll();
}
增加其映射文件,注:省略头部的一些引入代码
select * from account
还是要再多啰嗦一句,
resultType="Account"
这里是因为我们在主配置文件中已经,对omain中类都起了别名,所以可以直接用包下的类名,如果不了解的朋友,使用全类名也是一样的测试一下:
/**
* 测试查询所有
*/
@Test
public void testFindAll(){
List accounts = accountMapper.findAll();
for (Account account : accounts){
System.out.println(account);
}
}
看一下效果:
文章图片
image (四) Account 一对一查询 如何查询到 Acount 中信息的同时,根据用户的 id 值将对应的数据显示出来,这其实主要就是需要改变 SQL 的写法,我们在本地的 MySQL中先试一试
SELECT FROM account a,user u WHERE u.id=a.uid;
执行结果
文章图片
image 结果出来了,但是 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 这样看起来就条理了许多
到了这一步,我们就可以在代码中实现这样的功能了,即通过查询账户信息,同时查询出对应的用户信息,那由于注册时间,男女等信息,我并不想要,怎么办呢?我们可以再加一点约束,用户的信息只显示名称和地址两个字段
A:创建子类方式(不算太常用)
(1) 修改 Account 接口
/**
* 查询所有账户,并且带有用户名称和地址信息
* @return
*/
List findAllAccount();
大家可能注意到我们返回的 List 类型为 UserAccount,这是为什么呢?
【千字文带你入门-MyBatis多表查询】既然我们想返回的信息中,需要包含两个表中的信息,似乎我们并没有一个实体可以承载这么多信息,所以我们创建一个 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 a.*,u.username,u.address from account a , user u where u.id = a.uid;
(4) 测试代码
/**
* 查询所有账户,并且带有用户名称和地址信息
* @return
*/
@Test
public void testFindAllAccount(){
List uas = accountMapper.findAllAccount();
for (UserAccount ua : uas ){
System.out.println(ua);
}
}
(5) 执行效果
文章图片
image B:建立实体类关系方式(推荐)
(1) 修改 Account 接口
/**
* 查询所有账户
* @return
*/
List 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
SELECT u.*,a.id AS aid,a.uid,a.money FROM account a,user u WHERE u.id = a.uid;
说明:由于我们想要返回的结果为多个值,是没有一个完全与返回结果值一一匹配的封装类去接收的,所以我们可以使用MyBatis 提供的 resultMap 进行接收结果数据,它会在列名和 Java 包装类属性名之间创建映射关系,这篇文章的重心还是放在表的操作上,关于这个问题,以后可以专门写篇文章进行说明,如果对这部分不清楚的朋友,可以自己查一下这些标签的意义,实际上不会太过于复杂的
(4) 测试代码
/**
* 测试查询所有
*/
@Test
public void testFindAll(){
List accounts = accountMapper.findAll();
for (Account account : accounts){
System.out.println("--------------------------");
System.out.println(account);
System.out.println(account.getUser());
}
}
(5) 执行效果
文章图片
image (五) User 一对多查询 (1) 修改 UserMapper 接口
/**
* 查询所有用户信息,同时显示出该用户下的所有账户
*
* @return
*/
List findAll();
(2) 修改 User 类 在 Java 类中应该添加一个集合成员,类型为 Account,方便我们承载账户的信息
//一对多关系映射,主表实体应该包含从表实体的集合引用
private List accounts;
public List getAccounts() {
return accounts;
}public void setAccounts(List accounts) {
this.accounts = accounts;
}
(3) 修改 AccountMapper.xml
SELECT * FROM user u LEFT OUTER JOIN account a on u.id = a.uid;
注:
LEFT OUTER JOIN
:左外连接,可以将左表的数据全部显示出来(4) 测试代码
/**
* 测试查询所有
*/
@Test
public void testFindAll() {
List users= userMapper.findAll();
for (User user : users) {
System.out.println("--------------------------");
System.out.println(user);
System.out.println(user.getAccounts());
}
}
(5) 执行效果
文章图片
image 可以看到,所有用户信息被打印了出来(上图只截取了前面的部分),并且在用户下存在所有账户的信息也被打印了出来
(六) 多对多操作 前面我们看完了,用户以及账户之间一对多的关系,下面我们来研究一下多对多的情况,这种,情况会麻烦一些,例如我们举个例子:用户以及职位之间的关系
- 一个用户可以有多个职位,而一个职位也可以属于多个用户
首先创建一个职位表
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;
insertinto `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;
insertinto `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 findAll();
}
(3) 创建 RoleMapper.xml
SELECT * FROM role
需要特别注意的是:column中的值是数据库中字段名,而property中的值是JavaBean中的对应成员变量,由于两者的名字并不是相同的,所以请注意区分
(4) 测试代码
@Test
public void testFindAll(){
List roles = roleMapper.findAll();
for (Role role : roles){
System.out.println("-----------------------");
System.out.println(role);
}
}
(5) 执行效果
文章图片
image B:查询角色获取对应职位
(1) 修改 Role 类 在 Role 实体类中增加 User 类型的 List集合
//多对多关系映射,一个职位可以拥有多个用户
private List users;
public List getUsers() {
return users;
}public void setUsers(List users) {
this.users = users;
}
接口方法我们仍然用前面创建的,findAll 方法
(2) 修改 RoleMapper.xml 在这部分,毫无疑问,需要创建 Role 的 resultMap ,还要一部分,就是对应 SQL 语句的编写
SQL语句的编写我们需要简单的分析一下,首先看一下,三张表之间的关系
文章图片
image 中间表通过UID RID两个字段分别连接起了 user 和 role 两张表
先通过 role 表中的 id 找到 中间表的 rid 然后通过 rid 对应的 uid值 找到 user 表中的 id 值,从而获取到对应的用户信息
这个时候我们需要两个左外连接,xml 代码如下
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
(3) 测试代码
@Test
public void testFindAll(){
List roles = roleMapper.findAll();
for (Role role : roles){
System.out.println("---------------------");
System.out.println(role);
System.out.println(role.getUsers());
}
}
(4) 执行效果
文章图片
image C:查询职位获取对应用户
(1) 修改接口方法
public interface UserMapper {
/**
* 查询所有用户信息,同时显示出该用户下的所有账户
*
* @return
*/
List findAll();
}
(2) 修改 User 实体 这是多对多的关系映射,一个用户可以具备多个角色
private List roles;
public List getRoles() {
return roles;
}public void setRoles(List roles) {
this.roles = roles;
}
(3) 修改 RoleMapper.xml
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
(4) 测试代码
@Test
public void testFindAll(){
List users = userMapper.findAll();
for (User user : users){
System.out.println("---------------------");
System.out.println(user);
System.out.println(user.getRoles());
}
}
(5) 执行效果
文章图片
image 结尾 如果文章中有什么不足,欢迎大家留言交流,感谢朋友们的支持!
如果能帮到你的话,那就来关注我吧!如果您更喜欢微信文章的阅读方式,可以关注我的公众号
在这里的我们素不相识,却都在为了自己的梦而努力 ?
一个坚持推送原创开发技术文章的公众号:理想二旬不止
文章图片
image
推荐阅读
- typeScript入门基础介绍
- Android|Android sqlite3数据库入门系列
- Android下的IO库-Okio源码解析(一)|Android下的IO库-Okio源码解析(一) 入门
- 深度学习-入门
- 第三章|第三章 进校园重拾旧梦 登讲台初为人师第一节 接乱班面临考验 ,遇高师指点入门
- iOS开发技术之美—iOS入门技术的基础学习
- OpenCV|OpenCV-Python实战(18)——深度学习简介与入门示例
- 【入门】Python网络爬虫与信息提取1
- 垂直农场101-室内农业入门知识
- 所有Python入门书籍的整理,初学者必看,附赠所有电子版(三)