MyBatis从入门到精通(十一):MyBatis高级结果映射之一对多映射
本篇博客主要讲解MyBatis中如何使用collection标签实现查询结果一对多映射。
1. 使用collection标签
需求:根据用户id查询用户信息的同时获取用户拥有的角色,一个用户可以拥有1个或多个角色。
一般情况下,不建议直接修改数据库表对应的实体类。
所以这里我们延用之前博客中新建的类SysUserExtend,并添加如下代码,如下所示:
/**
* 用户的角色集合
*/
private List<SysRole> sysRoleList;
public List<SysRole> getSysRoleList() {
return sysRoleList;
}
public void setSysRoleList(List<SysRole> sysRoleList) {
this.sysRoleList = sysRoleList;
}
然后,我们在接口SysUserMapper中添加如下方法:
/**
* 获取所有的用户以及对应的所有角色
*
* @return
*/
List<SysUserExtend> selectAllUserAndRoles();
接着,在对应的SysUserMapper.xml中添加如下代码:
<resultMap id="userRoleListMap" type="com.zwwhnly.mybatisaction.model.SysUserExtend" extends="sysUserMap">
<collection property="sysRoleList" columnPrefix="role_"
ofType="com.zwwhnly.mybatisaction.model.SysRole">
<id property="id" column="id"/>
<result property="roleName" column="role_name"/>
<result property="enabled" column="enabled"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
</collection>
</resultMap>
因为我们在前面的博客中已经建过角色表的roleMap:
<resultMap id="roleMap" type="com.zwwhnly.mybatisaction.model.SysRole">
<id property="id" column="id"/>
<result property="roleName" column="role_name"/>
<result property="enabled" column="enabled"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
</resultMap>
所以上面的collection标签可以简化为:
<collection property="sysRoleList" columnPrefix="role_"
resultMap="com.zwwhnly.mybatisaction.mapper.SysRoleMapper.roleMap">
</collection>
新建接口对应的查询代码,使用上面新建的userRoleListMap,如下所示:
<select id="selectAllUserAndRoles" resultMap="userRoleListMap">
SELECT
u.id,
u.user_name,
u.user_password,
u.user_email,
u.create_time,
r.id role_id,
r.role_name role_role_name,
r.enabled role_enabled,
r.create_by role_create_by,
r.create_time role_create_time
FROM sys_user u
INNER JOIN sys_user_role ur ON u.id = ur.user_id
INNER JOIN sys_role r ON ur.role_id = r.id
</select>
最后,在SysUserMapperTest测试类中添加如下测试方法:
@Test
public void testSelectAllUserAndRoles() {
SqlSession sqlSession = getSqlSession();
try {
SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class);
List<SysUserExtend> sysUserList = sysUserMapper.selectAllUserAndRoles();
System.out.println("用户数:" + sysUserList.size());
for (SysUserExtend sysUser : sysUserList) {
System.out.println("用户名:" + sysUser.getUserName());
for (SysRole sysRole : sysUser.getSysRoleList()) {
System.out.println("角色名:" + sysRole.getRoleName());
}
}
} finally {
sqlSession.close();
}
}
运行测试代码,测试通过,输出日志如下:
DEBUG [main] - ==> Preparing: SELECT u.id, u.user_name, u.user_password, u.user_email, u.create_time, r.id role_id, r.role_name role_role_name, r.enabled role_enabled, r.create_by role_create_by, r.create_time role_create_time FROM sys_user u INNER JOIN sys_user_role ur ON u.id = ur.user_id INNER JOIN sys_role r ON ur.role_id = r.id
DEBUG [main] - ==> Parameters:
TRACE [main] - <== Columns: id, user_name, user_password, user_email, create_time, role_id, role_role_name, role_enabled, role_create_by, role_create_time
TRACE [main] - <== Row: 1, admin, 123456, admin@mybatis.tk, 2019-06-27 18:21:07.0, 1, 管理员, 1, 1, 2019-06-27 18:21:12.0
TRACE [main] - <== Row: 1, admin, 123456, admin@mybatis.tk, 2019-06-27 18:21:07.0, 2, 普通用户, 1, 1, 2019-06-27 18:21:12.0
TRACE [main] - <== Row: 1001, test, 123456, test@mybatis.tk, 2019-06-27 18:21:07.0, 2, 普通用户, 1, 1, 2019-06-27 18:21:12.0
DEBUG [main] - <== Total: 3
用户数:2
用户名:admin
角色名:管理员
角色名:普通用户
用户名:test
角色名:普通用户
2. MyBatis合并规则
观察上面的日志,我们的Sql语句查询到了3条数据,在数据库查询的话,也是返回如下的数据:
但经过MyBatis配置的映射到,最后合并为了2个用户,其中第1个用户包含了2个角色,第2个用户包含了1个角色,那么MyBatis是根据什么规则合并的呢?
MyBatis在处理结果的时候,会判断结果是否相同,如果是相同的结果,则只会保留第一个结果,所以关键点就是MyBatis如何判断结果是否相同。
判断结果是否相同时,最简单的情况就是在映射配置中至少有1个id标签,上面使用的sysUserMap就配置了id标签:
<id property="id" column="id"/>
一般情况下,id标签配置的字段为表的主键,如果是联合主键,可以配置多个id标签。
id标签的作用就是在嵌套的映射配置时判断数据是否相同,当配置id标签时,MyBatis只需要逐条比较所有数据中id标签配置的字段值是否相同即可。
也可以不配置id标签,将上面的代码修改为:
<result property="id" column="id"/>
使用result不会影响查询结果,但是此时MyBatis就要对所有字段进行比较,因此当字段数为M时,如果查询结果有N条,就需要比较M*N次,如果配置了id标签,只需要比较N次即可,所以要尽可能的配置id标签。
结合上面的例子,因为Sql的查询结果中,前2条数据中用户的id是相同的,所以会合并为1个用户,所以最终的结果是2个用户。
为了更清楚的理解id标签的作用,我们将sysUserMap临时修改为:
<resultMap id="sysUserMap" type="com.zwwhnly.mybatisaction.model.SysUser">
<id property="userPassword" column="user_password"/>
<result property="id" column="id"/>
<result property="userName" column="user_name"/>
<result property="userEmail" column="user_email"/>
<result property="userInfo" column="user_info"/>
<result property="headImg" column="head_img" jdbcType="BLOB"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
</resultMap>
运行测试方法,输出的部分日志如下:
用户数:1
用户名:admin
角色名:管理员
角色名:普通用户
因为3个用户的密码都是123456,所以查询到的3条数据只保留了第一个用户admin,包含了2个角色。
有的同学也许会问,为什么不是拥有3个角色呢?
这是因为MyBatis会对嵌套查询的每一级对象都进行属性比较,MyBatis会先比较顶层的对象,如果SysUser部分相同,就继续比较SysRole部分,如果SysRole不同,就会增加一个SysRole,如果相同就保留前一个。
如果SysRole还有下一级,依次按照规则去比较。
上面的“普通用户”角色重复了,所以只保留了前1个,导致最终的结果中只包含2个角色而不是3个。
3. 源码及参考
源码地址:https://github.com/zwwhnly/mybatis-action.git,欢迎下载。
刘增辉《MyBatis从入门到精通》
原创不易,如果觉得文章能学到东西的话,欢迎点个赞、评个论、关个注,这是我坚持写作的最大动力。
如果有兴趣,欢迎添加我的微信:zwwhnly,等你来聊技术、职场、工作等话题(PS:我是一名奋斗在上海的程序员)。
MyBatis从入门到精通(十一):MyBatis高级结果映射之一对多映射的更多相关文章
- MyBatis从入门到精通(九):MyBatis高级结果映射之一对一映射
最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 本篇博客主要讲解MyBatis中实现查 ...
- MyBatis从入门到精通(一):MyBatis入门
最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 1. MyBatis简介 2001 ...
- MyBatis从入门到精通(1):MyBatis入门
作为一个自学Java的自动化专业211大学本科生,在学习和实践过程中"趟了不少雷",所以有志于建立一个适合同样有热情学习Java技术的参考"排雷手册". 最近在 ...
- MyBatis从入门到精通(二):MyBatis XML方式的基本用法之Select
最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 1. 明确需求 书中提到的需求是一个基 ...
- MyBatis从入门到精通(三):MyBatis XML方式的基本用法之多表查询
最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 1. 多表查询 上篇博客中,我们示例的 ...
- MyBatis从入门到精通(四):MyBatis XML方式的基本用法之增删改
最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 1. insert用法 1.1 简单的 ...
- MyBatis从入门到精通(五):MyBatis 注解方式的基本用法
最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 1. @Select 注解 1.1 使 ...
- MyBatis从入门到精通(六):MyBatis动态Sql之if标签的用法
最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 本篇博客主要讲解如何使用if标签生成动 ...
- MyBatis从入门到精通(七):MyBatis动态Sql之choose,where,set标签的用法
最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 本篇博客主要讲解如何使用choose, ...
随机推荐
- 数据绑定(三)为Binding指定绑定源的几种方法
原文:数据绑定(三)为Binding指定绑定源的几种方法 Binding的源是数据的来源,所以,只要一个对象包含数据并能通过属性把数据暴露出来,它就能当作Binding的源来使用,常用的办法有: 一. ...
- Android adb shell 无法启动:insufficient permissions for device
解决办法1:lsusb查看vendorId号,然后在/etc/udev/rules.d/目录下增加(或修改)51-android.rules文件.增加一条记录:SUBSYSTEM=="usb ...
- ExtJS 折线图趟过的坑
问题: 1.根据条件检索后绘制折线图,之前的坐标没有清除如图 解决方案: 在绘制之前,清空坐票: leftLine.surface.removeAll(); leftLine.redraw(false ...
- CUDA配置
你问这个有多恶心,是真的很恶心!!! 首先推出一个博客上的内容,里面内容很不错,都是前车之鉴,很有用.http://blog.csdn.net/masa_fish/article/details/51 ...
- Bitmap的读写和几个小儿科的滤镜效果~
闲来玩玩图像处理,拿破仑说过:“不想自己实现滤镜的美工不是好程序员~~#@!*^...#&!@......” 因为在学校做过很多美工的工作,而且从小就喜欢画画所以对图像相关的东西都还比较感兴 ...
- WIN8安装oracle11g时出现不满足最低配置解决办法
Windows8上面安装Oracle11g客户端和服务端时都会出现这样的错误提示:[INS-13001]环境不满足最低要求 产生这种报错的主要原因在于:oracle 11g的配置文件中并没有提供匹配w ...
- AI行为树的工作原理
很久没写博客了,最近在项目刚部署到测试服,需要进行压测,老大相当专业的用了行为树来组织压测机器人的代码,这段时间陆陆续续在网上看了不少关于行为树的文章,其中有一篇我觉得写得非常到位,它原文是英文,链接 ...
- 【工具】java 文本文档txt写出记录工具
彩蛋!http://abowman.com/google-modules/dog/ 以下是自己小游戏生成人物经历的传记时保存txt所用到的工具类,功能简单,不多说什么,贴上代码: package co ...
- 面试中的作用域题和THIS 指向的问题
作用域的面试题 1. fn() function fn () { console.log(12) } var as = function () { console.log(45) } 2. var a ...
- Product Backlog:终极任务清单
健康的Product Backlog就像一个健康的人那样:整洁有序.组织合理.公开透明.一个按照优先级顺序排好的敏捷Backlog不仅能够简化发版和迭代计划,还能够对团队计划去做的所有工作进行细致规划 ...