MyBatis多表操作
前言
在前面的两个小节里,我们已经初步接触到MyBatis,并且通过MyBatis实现了单表的增删改查操作,但在实际开发过程中,经常遇到的是多表之间的操作,MyBatis在多表操作方面也提供非常方便的工具用于将结果集映射到对象中,这一节,我们将详细学习这一部分。
多表操作
由于本节涉及到多表操作,在前面建立的数据表明显不符合,所以这里我们需要再建立一些表以及插入一些数据
本节所使用的表以及数据均来自刘增辉老师的《MyBatis从入门到精通》
create table sys_user ( id bigint not null auto_increment comment '用户ID', user_name varchar(50) comment '用户名', user_password varchar(50) comment '密码', user_email varchar(50) comment '邮箱', create_time datetime comment '创建时间', primary key (id) );alter table sys_user comment '用户表';create table sys_role ( id bigint not null auto_increment comment '角色ID', role_name varchar(50) comment '角色名', enabled int comment '有效标志', create_by bigint comment '创建人', create_time datetime comment '创建时间', primary key (id) );alter table sys_role comment '角色表';create table sys_privilege ( id bigint not null auto_increment comment '权限ID', privilege_name varchar(50) comment '权限名称', privilege_url varchar(200) comment '权限URL', primary key (id) );alter table sys_privilege comment '权限表';create table sys_user_role ( user_id bigint comment '用户ID', role_id bigint comment '角色ID');alter table sys_user_role comment '用户角色关联表';create table sys_role_privilege ( role_id bigint comment '角色ID', privilege_id bigint comment '权限ID');alter table sys_role_privilege comment '角色权限关联表';
测试数据
insert into `sys_user` values (1, 'admin', '123456', 'admin@mybatis', '管理员', null, now()), (1001, 'test', '123456', 'test@mybatis', '测试用户', null, now());insert into sys_role values (1, '管理员', '1', '1', now()), (2, '普通用户', '1', '1', now());insert into sys_user_role values (1, 1), (1, 2), (1001, 2);insert sys_privilege values (1, '用户管理', '/users'), (2, '角色管理', '/roles'), (3, '系统日志', '/logs'), (4, '人员维护', '/persons'), (5, '单位维护', '/companies');insert sys_role_privilege values (1, 1), (1, 3), (1, 2), (2, 4), (2, 5);
对应的实体类根据数据库的字段建立就好了。
关于每个表的单表操作,在前面一个小节已经研究过了,所以在这个小节里,就不演示单表的操作了。
多表操作,本质上其实就是连接多个表,然后查询出数据,根据关联对象之间的关系,又可以分为1对1操作,1对多操作,多对多操作(本质上而言其实也是1对多),所以接下来,我们分两个部分来看如何通过MyBatis来操作
1对1操作
假设我们要根据用户的ID查询出用户的角色,并且假定一个用户只有一个角色(当然,实际上不止),这里以1001号用户为例,其在数据库中也仅有一个角色,所以符合我们操作的要求。
为了能通过MyBatis自动封装,我们在SysUser
中增加一个字段SysRole
public class SysUser { // 其他字段与数据库保持一致即可 private SysRole role; // set() get() toString()}
在查询操作中,我们可以通过下面的方式来获取数据
<select id="selectUserAndRoleById" resultType="domain.SysUser"> select u.id, u.user_name userName, u.user_password userPassword, u.user_email userEmail, u.create_time createTime, <!-- 注意从这里开始的别名是"role.XXX",因为字段中是role 为了能够自动注入,所以需要采用obj.attr的形式, 如果有多级对象,则是 a.b.c这种形式 --> r.id "role.id", r.role_name "role.roleName", r.enabled "role.enable", r.create_by "role.createBy", r.create_time "role.createTime" from sys_user u join sys_user_role ur on u.id = ur.user_id join sys_role r on r.id = ur.role_id where u.id = #{id}</select>
上面的实现方式从结果来看是没有问题的,但是从工程的角度来讲,其实不太好,尤其是当存在多个不同类型的查询,比如根据ID,根据名称,根据邮箱地址等,我们需要编写多份的代码,并且其中的select部分基本上是不变的,也就是带来非常明显的冗余了。
更好地解决方案是使用MyBatis中的resultMap
,通过resultMap
来封装,可以实现代码复用的目的
<resultMap id="userRoleMap" type="domain.SysUser"> <id property="id" column="id"/> <!--其他的字段--> <result property="role.id" column="r.id"/> <!--其他的字段--></resultMap><select id="selectUserAndRoleById" resultMap="userRoleMap"> ... 这里根据对应的字段调整一下,只需要能正确映射就行</select>
不过上面的内容语义不明显,更好的方式是使用resutlMap
的<association>
标签来关联对象,如下
<resultMap id="userRoleMap" type="domain.SysUser"> <id property="id" column="id"/> <!--其他的字段--> <!-- 注意这里,使用的是association,association的使用跟resultMap是类似的 并且使用多了columnPrefix属性,为了区分来自不同表的字段, 如果是多级的嵌套,则需要指定多级,如 role_pri_XXX,一个层次的columnPrefix会 去过滤每一次匹配的前缀 当然,在查询的时候也需要将对应的前缀标注出来 --> <association property="role" javaType="domain.SysRole" columnPrefix="role_"> <result property="id" column="id"/> <!--其他的字段--> </association></resultMap><!--注意下面的内容 role_也即是columnPrefix=""中指定的字段--><select> 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</select>
通过上面的方式,当需要的时候,就可以直接指定查询的resultMap="userRoleMap"
即可,已经减少了一部分的重复操作了,但是,上面的方式仍然不是合适的,因为既然有user对应的map,那实际上将role对应的字段也封装到map中,然后直接调用即可,这样,多个使用到role的地方都可以直接使用了
首先在SysRoleMapper.xml定义对应的roleMap,当然,放在其他的mapper里也是可以,但是放在SysRoleMapper.xml是最合适的
<mapper namespace="mapper.SysRoleMapper"> <resultMap id="roleMapper" type="domain.SysRole"> <id property="id" column="id"/> <!--其他的字段--> </resultMap></mapper>
整理完之后的userRoleMap
内容如下
<resultMap id="userRoleMap" type="domain.SysUser"> <id property="id" column="id"/> <!--其他的字段--> <!--这里使用resultMap来指定其他的resultMap,如果不在本文件,则使用全限定名--> <association property="role" columnPrefix="role_" resultMap="mapper.SysRoleMapper.roleMapper"/></resultMap>
经过上面的整理之后,现在的整体结构就变得非常灵活了,特别是当我们需要组合多个对象的时候,通过这种方式,可以实现只需要定义一个resultMap,然后在多处使用
1对多操作
有了上面封装1对1的操作过程作为基础,实现一对多就容易很多了,只需要将<association>
替换为<collection>
即可,当然,由于上面为了方便,直接在SysUser中定义了一个SysRole对象,但实际上我们知道,一个用户是可以对应多个角色的,所以,在SysUser中应该定义的是一个SysRole容器,比如list或者set等,也就是实际上1对多的操作啦
<resultMap id="userRoleMap" type="domain.SysUser"> <!--注意这里--> <collection property="role" columnPrefix="role_" resultMap="mapper.SysRoleMapper.roleMapper"/></resultMap>
可以看到,因为为role对象定义roleMap,所以,当改动userRole时,其他的内容完全不需要改动
一个完整的例子
在上面的两步操作中,我们已经充分体验到了MyBatis中的resultMap
、assocation
以及collection
提供的便利,下面我们通过完整的例子,来加深对其认识
这里通过用户ID,获取其所有的角色以及所有角色对应的权限
将对应的实体类调整为如下
SysUser
public class SysUser { // 一个用户可能对应多个角色 private List<SysRole> role; }
作者:颜洛滨
链接:https://www.jianshu.com/p/fab0985b103a
共同学习,写下你的评论
评论加载中...
作者其他优质文章