mybatis进阶案例之多表查询

一、mybatis中表之间的关系

在数据库中,实体型之间的关系主要有如下几种:

1、一对一

如果对于实体集A中的每一个实体,实体集B中至多有一个(也可以没有)实体与之联系,反之亦然,则称实体集A与实体集B具有一对一联系,记为1:1 。例如,一个班级只有一个正班长,一个班长只在一个班中任职。

2、一对多

如果对于实体集A中的每一个实体,实体集B中有n个实体(n≥0)与之联系,反之,对于实体集B中的每一个实体,实体集A中至多只有一个实体与之联系,则称实体集A与实体集B有一对多联系,记为1:n。例如,一个班级中有若干名学生,每个学生只在一个班级中学习。

3、多对多

如果对于实体集A中的每一个实体,实体集B中有n个实体(n≥0)与之联系,反之,对于实体集B中的每一个实体,实体集A中也有m个实体(m≥0)与之联系,则称实体集A与实体B具有多对多联系,记为m:n。例如,一门课程同时有若干个学生选修,一个学生可以同时选修多门课程。

二、mybatis一对一查询

1.在数据库中新建account表

DROP TABLE IF EXISTS `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;insert  into `account`(`ID`,`UID`,`MONEY`) values (1,46,1000),(2,45,1000),(3,46,2000);

2.在入门案例的基础上,新建maven工程

1.在domain包下新建实体类Account,注意implements Serializable接口(之前的代码都没注意要实现序列化接口,具体可以参考谈谈序列化—实体bean一定要实现Serializable接口?[1])。

public class Account implements Serializable {private Integer id;private Integer uid;private Double money;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public Integer getUid() {return uid;}public void setUid(Integer uid) {this.uid = uid;}public Double getMoney() {return money;}public void setMoney(Double money) {this.money = money;}@Overridepublic String toString() {return "Account{" +"id=" + id +", uid=" + uid +", money=" + money +'}';}
}

2.创建dao接口

在dao包下新建DAO接口IAccountDao,如下:

public interface IAccountDao {/*** 查询所有账户* @return*/List<Account> findAll();
}

3.配置映射配置文件

在IUserDao.xml的同级目录下新建文件IAccountDao.xml文件,如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="dao.IAccountDao"><!-- 配置查询所有 --><select id="findAll" resultType="domain.Account">select * from account;</select>
</mapper>

4.在主配置文件中指定映射配置文件

在主配置文件SqlMapConfig.xml中指定IAccountDao.xml的位置,如下:

<!-- 配置映射文件的位置 -->
<mappers><mapper resource="dao/IUserDao.xml"></mapper><mapper resource="dao/IAccountDao.xml"></mapper>
</mappers>

5.添加测试函数

在MybatisTest类的同级目录下创建测试类AccountTest,如下:

public class AccountTest {private InputStream in;private SqlSession sqlSession;private IAccountDao accountDao;@Before//用于在测试方法执行之前运行public void init() throws IOException {//1.读取配置文件,生成字节输入流in = Resources.getResourceAsStream("SqlMapConfig.xml");//2.获取SqlSessionFactory对象SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);//3.获取SqlSession对象sqlSession = factory.openSession();//4.获取dao的代理对象accountDao = sqlSession.getMapper(IAccountDao.class);}@After//用于在测试方法执行之后运行public void close() throws IOException {//5.提交事务sqlSession.commit();//6.释放资源sqlSession.close();in.close();}/*** 测试查询所有*/@Testpublic void testFindAll() throws IOException {//执行查询所有方法List<Account> accounts = accountDao.findAll();for (Account account : accounts) {System.out.println(account);}}
}

6.运行测试函数

这个运行只是查询account表单独的信息,还只是单表查询,并非多表查询。结果如下:

Account{id=1, uid=41, money=1000.0}
Account{id=2, uid=45, money=1000.0}
Account{id=3, uid=41, money=2000.0}

3.开始一对一多表查询

如果我们想查询和一个人相关的个人信息和账户信息,就需要同时对account表和user表进行查询。一种可行的思路是:新建一个类,这个类包括一个user对象和account对象,然后将查询结果封装到这个对象中。这里我们让这个类继承自Account类,然后增加username和address两个属性。

1.在domain包下新建AccountUser类,如下:

public class AccountUser extends Account {private String username;private String address;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}@Overridepublic String toString() {return super.toString() +"   AccountUser{" +"username='" + username + ''' +", address='" + address + ''' +'}';}
}

2.在IAccountDao接口中,添加查询方法

/*** 查询账户和用户信息* @return*/
List<AccountUser> findUserAndAccount();

3.配置IAccountDao.xml

<!-- 配置查询用户和账户信息 -->
<select id="findUserAndAccount" resultType="domain.AccountUser">SELECT  user.username, user.address, account.* from user, account WHERE user.id = account.UID
</select>

4.在AccountTest类中测试

/*** 测试查询账户和用户信息* @throws IOException*/
@Test
public void testFindUserAndAccount() throws IOException {List<AccountUser> ausers = accountDao.findUserAndAccount();for (AccountUser auser : ausers) {System.out.println(auser);}
}
/**结果如下:
Account{id=1, uid=41, money=1000.0}   AccountUser{username='老王', address='北京'}
Account{id=2, uid=45, money=1000.0}   AccountUser{username='传智播客', address='北京金燕龙'}
Account{id=3, uid=41, money=2000.0}   AccountUser{username='老王', address='北京'}
*/

不过上述代码维护起来比较困难,因为继承会造成高耦合。一旦Account类修改,AccountUser类也要修改。因此我们还有一种方式来实现一对一查询。

在第二种方法中,我们通过标签来指定主从表的映射关系,并且在从表实体类中添加主表实体类的引用。

1.添加实体类引用

在domain包下的Account类中,添加如下代码

//从表实体应该包含主表实体的引用private User user;public User getUser() {return user;}public void setUser(User user) {this.user = user;}

2.添加查询方法

在dao包下的IAccountDao接口中添加对应的查询方法:

/*** 查询带有用户信息的账户* @return*/
List<Account> findAllAccountsWithUser();

3.配置查询结果和实体类属性的对应关系

在resources下的映射配置文件IAccountDao.xml文件中添加如下代码,其中为了解决Account类中id和User类中id重名的问题,我们在写sql语句时采取了对列名起别名的方式,将account.id重名为aid,具体可以参考MyBatis两张表字段名相同产生的问题[2]

<!-- 建立实体类的对应关系,id属性是resultMap的名称下, type是从表实体类型-->
<resultMap id="accountUserMap" type="domain.Account"><!-- column是查询结果的列名, property是对应实体类中的属性名 --><id column="aid" property="id"/><result column="uid" property="uid"/><result column="money" property="money"/><!-- association是用于指定从表方的引用实体属性的 --><!-- property是用于指定从表实体对主表实体引用的名称, javaType是主表实体类型 --><association property="user" javaType="domain.User"><!-- column是查询结果的列名, property是对应实体类中的属性名 --><id column="id" property="id"/><result column="username" property="username"/><result column="sex" property="sex"/><result column="birthday" property="birthday"/><result column="address" property="address"/></association>
</resultMap>

4.配置查询函数

在resources下的映射配置文件IAccountDao.xml文件中添加如下代码:

<!-- 配置查询带有用户信息的账户信息 -->
<select id="findAllAccountsWithUser" resultMap="accountUserMap">select user.*, account.id aid, account.uid, account.money from user, account WHERE user.id = account.UID
</select>

5.进行测试

在test目录下的AccountTest类中,添加测试方法:

/*** 测试查询带有用户信息的账户信息* @throws IOException*/
@Test
public void testFindAllAccountsWithUser() throws IOException {List<Account> accounts = accountDao.findAllAccountsWithUser();for (Account account : accounts) {System.out.println(account);System.out.println(account.getUser());}
}
/**
结果如下:
Account{id=1, uid=41, money=1000.0}
User{id=41, username='老王', address='北京', sex='男', birthday=Wed Feb 28 07:47:08 CST 2018}
Account{id=2, uid=45, money=1000.0}
User{id=45, username='传智播客', address='北京金燕龙', sex='男', birthday=Mon Mar 05 02:04:06 CST 2018}
Account{id=3, uid=41, money=2000.0}
User{id=41, username='老王', address='北京', sex='男', birthday=Wed Feb 28 07:47:08 CST 2018}
*/

三、mybatis一对多查询

一对多查询和多对多查询的步骤非常类似,具体如下:

1.在主表实体中添加从表实体的引用

在domain包下的User类中添加如下代码,由于是一对多的关系,所以添加的是列表引用:

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

2.添加查询方法

在dao包下的IUserDao接口中添加对应的查询方法:

/*** 查询带有账户信息的用户信息* @return*/
List<User> findUserWithAccounts();

3.配置对应关系

在resources下的映射配置文件IUserDao.xml文件中添加如下代码,同样需要解决重名问题:

<resultMap type="domain.User" id="userAccountsMap"><id column="id" property="id"></id><result column="username" property="username"/><result column="address" property="address"/> <result column="sex" property="sex"/><result column="birthday" property="birthday"/><!-- collection是用于建立一对多中集合属性的对应关系 --><!-- property是用于指定主表实体对从表实体引用的名称, ofType 用于指定集合元素的数据类型 --><collection property="accounts" ofType="domain.Account"><id column="aid" property="id"/><result column="uid" property="uid"/><result column="money" property="money"/></collection>
</resultMap>

4.配置查询方法

在resources下的映射配置文件IUserDao.xml文件中添加如下代码:

<!-- 配置查询带有账户信息的用户信息 -->
<select id="findUserWithAccounts" resultMap="userAccountsMap">select user.*, account.id aid, account.uid, account.money from user left outer join account on user.id = account.uid
</select>

5.测试

在test目录下的MybatisTest类中,添加测试方法:

@Test
public void testFindUserWithAccounts() {//6.执行操作List<User> users = userDao.findUserWithAccounts();for(User user : users) {System.out.println("-------每个用户的内容---------");System.out.println(user);System.out.println(user.getAccounts());}
}

查询结果如下:

user到account的一对多查询

四、mybatis多对多查询

1.在数据库中建立角色表role和关联表user_role

DROP TABLE IF EXISTS `role`;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,'校长','管理整个学校');DROP TABLE IF EXISTS `user_role`;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 (41,1),(45,1),(41,2);

2.建立实体类Role

在domain包下新建实体类Role,注意在该类中添加另一实体类的列表引用,代码如下:

public class Role implements Serializable {private Integer roleId;private String roleName;private String roleDesc;//对user对象集合的引用private List<User> users;public List<User> getUsers() {return users;}public void setUsers(List<User> users) {this.users = users;}public Integer getRoleId() {return roleId;}public void setRoleId(Integer roleId) {this.roleId = roleId;}public String getRoleName() {return roleName;}public void setRoleName(String roleName) {this.roleName = roleName;}public String getRoleDesc() {return roleDesc;}public void setRoleDesc(String roleDesc) {this.roleDesc = roleDesc;}@Overridepublic String toString() {return "Role{" +"roleId=" + roleId +", roleName='" + roleName + ''' +", roleDesc='" + roleDesc + ''' +'}';}
}

3.新建接口文件

在dao包下新建接口IRoleDao,代码如下:

public interface IRoleDao {/*** 查询所有带有用户信息的角色信息* @return*/List<Role> findRolesWithUsers();
}

4.编辑配置文件

在resources目录下新建映射配置文件IRoleDao.xml,主要配置如下:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dao.IRoleDao"><!--定义 role 表的 ResultMap--><resultMap id="roleUserMap" type="domain.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="domain.User"><id column="id" property="id"></id><result column="username" property="username"></result><result column="address" property="address"></result><result column="sex" property="sex"></result><result column="birthday" property="birthday"></result></collection></resultMap><!-- 配置查询带有用户信息的角色信息 --><select id="findRolesWithUsers" resultMap="roleUserMap">select user.*, role.id as rid, role.role_name, role.role_descfrom role left outer join user_role on role.id = user_role.ridleft outer join user on user.id = user_role.uid</select>
</mapper>

由于是根据角色信息查询带有用户信息,所以查询的sql语句中,连接的顺序是role left join user_role left join user。不能更改顺序。

别忘了在主配置文件SqlMapConfig.xml文件中的mappers标签中导入:

<mapper resource="dao/IRoleDao.xml"></mapper>

5.测试

在test目录下新建测试类RoleTest,代码如下:

public class RoleTest {private InputStream in;private SqlSession sqlSession;private IRoleDao roleDao;@Before//用于在测试方法执行之前运行public void init() throws IOException {//1.读取配置文件,生成字节输入流in = Resources.getResourceAsStream("SqlMapConfig.xml");//2.获取SqlSessionFactory对象SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);//3.获取SqlSession对象sqlSession = factory.openSession();//4.获取dao的代理对象roleDao = sqlSession.getMapper(IRoleDao.class);}@After//用于在测试方法执行之后运行public void close() throws IOException {//5.提交事务sqlSession.commit();//6.释放资源sqlSession.close();in.close();}/*** 测试查询所有*/@Testpublic void testFindRolesWithUsers(){List<Role> roles = roleDao.findRolesWithUsers();for(Role role : roles){System.out.println("---每个角色的信息----");System.out.println(role);System.out.println(role.getUsers());}}
}

查询结果如下:

role到user的多对多查询结果

mybatis多表查询出来的实体如何映射_mybatis进阶案例之多表查询相关推荐

  1. c3p0 参数 模糊查询_mybatis之动态sql,模糊查询,结果集处理,mybatis分页及特殊字符处理...

    目标及项目目录结构 目标 1.mybatis动态sql 2.模糊查询 3.查询返回结果集的处理 4.分页查询 5.特殊字符处理 项目的目录结构 1.mybatis动态sql If.trim.forea ...

  2. Mybatis多对多,复杂增删改查(特殊需求循环插入,分组查询)

    2021.8.31 从25号开始练习复杂的mybatis多对多,从设计数据库思路到实现需求功能转移到实体项目中 1.之前很少看过字符转换的详细内容从今往后会注意字符串转换此项目为转数组(date)实体 ...

  3. Mr.张小白(案例:学生信息查询系统的MyBatis的实现)

    学生信息查询系统 一.步骤 1.引入相关依赖pom.xml <?xml version="1.0" encoding="UTF-8"?> <p ...

  4. mybatis-plus/mybatis的组件们——拦截器、字段填充器、类型处理器、表名替换、SqlInjector(联合主键处理)

    最近有个练手的小例子,大概就是配置两个数据源,从一个数据源读取数据写到另一个数据源,虽然最后做了出来,但是不支持事务...就当是对mybatis-plus/mybatis组件使用方式的记录吧,本次例子 ...

  5. 设置Mybatis(3.2.8)实体嵌套关系(一对多,多对多)遇到的问题及经验总结记录...

    2019独角兽企业重金招聘Python工程师标准>>> 原始目标: 配置好mapper,使得可以在实体中表示表之间的联系(一个表外键,用另一个实体表示) 深读了mybatis 官方的 ...

  6. C#中怎样连接数据库并将查询结果转为实体类以及如何加入事务

    场景 新建一个程序,需要对数据的表进行查询并将查询结果转换为实体类,然后将多个实体类 再插入到另一个数据库的表中,执行插入的过程中要使用事务. 注: 博客主页: https://blog.csdn.n ...

  7. Linq 异常“此提供程序只支持对返回实体或投影(包含所有标识列)的有序查询使用 Skip()...”...

    问题:asp.net使用linq,在sql server 2005下面使用视图分页没有问题,但在sql server 2000下面使用视图 提示:此提供程序只支持对返回实体或投影(包含所有标识列)的有 ...

  8. mysql 自动生成mapper_Spring Boot整合mybatis并自动生成mapper和实体实例解析

    最近一直都在学习Java,发现目前Java招聘中,mybatis出现的频率挺高的,可能是目前Java开发中使用比较多的数据库ORM框架.于是我准备研究下Spring Boot和mybatis的整合. ...

  9. mysql一次查询无关联多个表_面试官:为什么mysql不建议执行超过3表以上的多表关联查询?...

    点关注,不迷路:持续更新Java架构相关技术及资讯热文!!! 概述 前段时间在跟其他公司DBA交流时谈到了mysql跟PG之间在多表关联查询上的一些区别,相比之下mysql只有一种表连接类型:嵌套循环 ...

最新文章

  1. 有限状态机HDL模板
  2. boost::core模块实现分配器分配提示
  3. 软件构造学习笔记-第十二周
  4. centos下升级jdk版本
  5. 判断学生成绩(保证其在0-100)c语言
  6. 【iCore4 双核心板_uC/OS-II】例程一:认识 uC/OS-II
  7. js数组操作大全(转)
  8. JavaScript深入之从原型到原型链 1
  9. 计算机论文答辩2分钟演讲稿,论文答辩演讲稿
  10. MIL自动化单元测试
  11. Codeforces Round #116 C.Letter
  12. 310569138 294609417 297440781 猪八戒上的骗子
  13. 文本相似性处理(好比论文查重)
  14. 自动化测试运行脚本(python)
  15. jupyter notebook如何显示行号?
  16. 练习牛客网笔试题--前端js--60-双色球机选一注
  17. 如何启动屏幕保护程序
  18. android杂凑算法,SM3密码杂凑算法分析
  19. LogLog基数估计算法学习与实现分析
  20. 『Java安全』反序列化-CC7反序列化漏洞POP链分析_ysoserial CommonsCollections7 PoC分析

热门文章

  1. Linux中各种锁原理概述
  2. Android Audio System 架构初探(好文)
  3. 编译Android指定JDK/OpenJdk版本
  4. ServiceManager学习框图
  5. $.ajax中contentType属性为“application/json”和“application/x-www-form-urlencoded”的区别...
  6. vue用户行为收集_Vue前端数据采集 埋点 追踪用户系列行为
  7. 腾讯云主机安全防护(云镜)/usr/local/qcloud/YunJing/YDEyes/YDService 卸载
  8. 下载和安装CUDA和Cudnn(图文详解)
  9. python如何下载os库_简谈下载安装Python第三方库的三种方法
  10. cesium 加载网格