Java EE数据持久化框架 • 【第3章 MyBatis高级映射】
全部章节 >>>>
本章目录
3.1 一对一映射
实际开发中经常要处理一对一、一对多的关系。
- 一辆汽车需要有一个引擎,一份订单对应一个客户,这些都是一对一的关系
- 一辆汽车有4个或更多个轮子,一个客户有多份订单,这些是一对多的关系。
权限系统中还存在着一个用户拥有多个角色、一个角色拥有多个权限这样复杂的嵌套关系,使用MyBatis的高级结果映射便可以轻松地处理这种一对一、一对多的关系。
3.1.1 自动化一对一映射
自动映射就是查询时通过列的别名让MyBatis自动将值匹配到对应的实体字段上。
假设在RBAC权限系统中,一个用户只能拥有一个角色,如何通过MyBatis一次查 询出两个表的数据自动化映射至实体存储?
- 查询sql语句需要关联查询出两个表(用户表、角色表)数据
- 如何将查询出来的结果映射至实体对象 用户对应SysUser实体 角色对应SysRole实体
用户对应SysUser实体,角色对应SysRole实体 分别是两个实体,方法只能返回一个
解决:在用户实体中添加一个角色实体属性,代表用户所对应的角色。
//用户实体类
public class SysUser{
private String username;
// 其他用户属性略
//新增加一个用于表示用户所属角色的实体 private SysRole role;
//省略getter和setter方法
//新增加一个用于表示用户所属角色的实体 private SysRole role; 因为一个用户拥有有一个角色
进行查询结果映射时,MyBatis会先将用户信息映射,然后查找role属性,如果存在role属性就创建role对象,然后在role对象中继续查找roleName,将列名role_name的值绑定到role对象的roleName属性。
接下来完成接口和对应Mapper.xml的SQL配置。 sql语句需要关联查询两个表(用户表、角色表)数据,注意列使用别名对应
示例:
//测试代码如下:
//获取UserMapper接口代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 这里假设id为2L的用户只有一个角色,调用方法得到用户
SysUser user = userMapper.selectUserAndRoleById(2L);
//此时该SysUser对象中包含了一个Role对象
System.out.println("用户名为"+user.getUserName()+"的用户所拥有的角色名为"
+user.getRole().getRoleName());
关联的嵌套结果映射需要关联多个表,将所有需要的值一次性查询出来。 这种方式的好处是减少数据库查询次数,减轻数据库的压力;
缺点是需要写比较复杂的SQL,并且当嵌套结果更复杂时,不容易一次写正确。 由于需要在应用服务器上将结果映射到不同的类上,因此,也会增加应用服务器的压力。
如果一定需要使用嵌套结果,并且整个复杂的SQL执行速度很快时,建议使用关联的嵌套结果映射。
3.1.2 <resultMap>标签配置一对一映射
除了使用MyBatis的自动映射来处理一对一嵌套结果映射,还可以在Mapper.xml 映射文件中配置resultMap结果映射:
示例:
<resultMap id="userRoleMap" type="SysUser">
<id property="id" column="id" />
<result property="userName" column="use_name" />
<!-- user相关属性映射字段略 -->
<!– 将user中的role对象相关属性映射列举出来,使用role前缀 -->
<result property="role.id" column="role_id" />
<result property="role.roleName" column="role_name " />
<result property="role.enabled" column="enabled" />
<result property="role.createBy" column="create_by" />
<result property="role.createTime" column="role_create_time" jdbcType="TIMESTAMP" />
</resultMap>
<select id="selectUserAndRoleById2" resultMap="userRoleMap">
select u.id, u.user_name, u.user_email, u.user_info, u.head_img,
u.create_time, r.id role_id, r.role_name, r.enabled, r.create_by,
r.create_time role_create_time
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>
resultMap="userRoleMap" 注意:因为有两个create_time列,所以增加了role_create_time区分
观察发现,在配置resultMap标签时非常繁琐,既配置了SysUser的映射,又增加了包含的SysRole角色对象的映射,而上一章节已经配置了SysUser的映射,可以进行继承重用,如下:
<resultMap id="userRoleMap" type="SysUser“ extends=“userMap”>
<!-- user相关属性映射字段无需再写-->
<!– 将user中的role对象相关属性映射列举出来,使用role前缀 -->
<result property="role.id" column="role_id" />
<result property="role.roleName" column="role_name " />
<result property="role.enabled" column="enabled" />
<result property="role.createBy" column="create_by" />
<result property="role.createTime" column="role_create_time" jdbcType="TIMESTAMP" />
</resultMap>
extends=“userMap” 继承了另外一个resultMap
3.1.3 <association>标签配置一对一映射
在<resultMap>标签中,可以使用子标签<association>对一个复杂的实体类型进行关联,它用于一对一的关联配置。比如用户中新增的SysRole实体,就可以使用它映射:
<resultMap id="userRoleMap" type="SysUser" extends="userMap">
<association property="role" columnPrefix="role_" javaType="SysRole">
<result 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" />
</association>
</resultMap>
property="role" 用户中存储角色的实体属性名
columnPrefix="role_" 防止列名冲突,角色列添加前缀
javaType="SysRole" 角色的实体类型
使用<association>标签时,配置了property属性为role,并且配置了columnPrefix列前缀为role_,所以查询时的SQL对应角色信息列时,别名增加role_前缀,如下:
示例:
<select id="selectUserAndRoleById2" resultMap="userRoleMap">
select u.id, u.user_name, u.user_email, u.user_info, u.head_img,
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 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>
result结果映射时,会自动加上role_前缀和这里对应
既然SysUser映射时可以使用extends继承而简化,那么SysRole可以吗?
示例:
<resultMap id="userRoleMap" type="SysUser" extends="userMap">
<association property="role" columnPrefix="role_"
resultMap="jack.mybatis.authority.mapper.RoleMapper.roleMap" />
</resultMap>
这里引用了角色RoleMapper中的映射定义
3.1.4 实践练习
3.2 一对一映射中的嵌套查询
3.2.1 <association>标签嵌套查询属性
前面已经实现了通过一个复杂的连接查询语句,查询出用户及对应角色的数据,并且成功实现了结果的映射。除了通过复杂的单条SQL查询获取结果,还可以利用简单的多条SQL通过多次查询转换为我们需要的结果。
实现思路如下(两个SQL语句):
- 根据用户ID查询用户的SQL语句及映射
- 根据用户对应的角色ID,查询该角色信息的SQL语句及映射
<association>标签的嵌套查询常用的属性如下:
select:另一个映射查询的id,MyBatis会额外执行这个查询获取嵌套对象的结果。
column:列名(或别名),将主查询中列的结果作为嵌套查询的参数。
fetchType:数据加载方式,可选值为lazy(懒散)和eager(急切),分别为延迟加载和立即加载。
3.2.2 <association>标签嵌套查询实现
示例:
根据用户id获取用户和该用户所拥有的角色信息。
//RoleMapper接口中,根据角色ID查询角色信息
SysRole selectRoleById(Long id);
//UserMapper接口中,根据用户ID查询用户信息
SysUser selectUserAndRoleByIdSelect(Long id);
<resultMap id="userRoleMapSelect" type="SysUser" extends="userMap">
<association property="role" column="{id=role_id}"
select="jack.mybatis.authority.mapper.RoleMapper.selectRoleById"/>
</resultMap>
<select id="selectUserAndRoleByIdSelect" resultMap="userRoleMapSelect">
select u.id, u.user_name, u.user_email, u.user_info, u.head_img,
u.create_time, ur.role_id from sys_user u join sys_user_role ur
on u.id=ur.user_id where u.id=#{id}
</select>
<!–查询User的时候仅查询了角色ID,然后利用该查询再单独查询出角色信息,并且赋予User实体中的role对象-->
<select id="selectRoleById" resultMap="roleMap">
select * from sys_role where id=#{id}
</select>
3.2.3 实践练习
3.3 一对多映射
3.3.1 <collection>标签实现集合映射
在一对多的关系中,主表的一条数据会对应关联表中的多条数据,因此一般查询时会查询出多个结果,所以在一的方向可以声明List集合来存储对应的多个数据。
<collection>标签实现集合的嵌套结果映射就是指通过一次SQL查询将所有的结果查询出来,然后通过配置的结果映射,将数据映射到不同的对象中去。
在BRAC权限系统中,一个用户拥有多个角色(注意前面在使用<association>标签是设定的特例,它限制一个用户只能拥有一个角色),而我们设计的初衷是一个用户可以拥有多个角色,所以获取用户及其所拥有的角色列表的步骤如下:
用户实体类中添加List集合用于存储该用户对应的多个角色,如下:
public SysUser{
// 其他原有属性省略
private List<SysRole> roleList; // 角色列表
//添加集合的setter和getter方法
// 其他原有字段的setter()方法和getter()方法
}
注意区分:前面是假设一个用户只能有一个角色时,使用的是角色对象
在UserMapper.xml定义id值为userRoleListMap的resultMap,如下:
<resultMap id="userRoleListMap" type="SysUser" extends="userMap">
<collection property="roleList" resultMap="jack.mybatis.authority.mapper.RoleMapper.roleMap" >
</collection>
</resultMap>
<collection>标签用于配置一对多关系,对应的属性必须是对象中的集合类型,因此这里是roleList。<resultMap>标签只是为了配置数据库字段和实体属性的映射关系,同时能存储一对多的数据结构肯定也能存储一对一关系,所以一对一是一对多的一种特例。
在映射器UserMapper新增方法selectAllUserAndRoles()方法,查询所有用户及其拥有的角色信息
List<SysUser> selectAllUserAndRoles();
在UserMapper.xml中新增selectAllUserAndRoles()方法,代码如下
<select id="selectAllUserAndRoles" resultMap="userRoleListMap">
select u.id, u.user_name, u.user_email, u.user_info, u.head_img,
u.create_time, r.id role_id, r.role_name, r.enabled, r.create_by,
r.create_time role_ create_time from sys_user u left join sys_user_role ur
on u.id=ur.user_id left join sys_role r on r.id=ur.role_id
</select>
测试selectAllUserAndRoles()方法,关键代码如下:
SqlSession sqlSession = getSqlSession();//获取SqlSession对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);//获取接口代理对象
List<SysUser> userList = userMapper.selectAllUserAndRoles();//得到所有用户列表
System.out.println("用户数:"+userList.size());
System.out.println("======================");
for (SysUser user : userList) { //循环用户列表中每个用户
System.out.println("用户名:"+user.getUserName());
for (SysRole role : user.getRoleList()) { //循环每个用户的角色信息
System.out.println("角色名:"+role.getRoleName());
}
System.out.println(“============结束============");
3.3.2 <association>标签和<collection>标签
<association>标签用于配置一对一、多对一时的对象信息,<collection>用于配置一对多时的集合信息,两者可以组合使用或者互相嵌套使用,以满足开发中不同的关联需求。
在配置角色时使用了roleMap,在roleMap中有两个字段create_by(创建人)和create_time(创建时间),将这两个字段所对应的实体属性createBy和createTime组封装成一个CreateRoleInfo,该类表示创建该角色的相关信息。
//分离角色的两个字段,独立形成一个类
public class CreateRoleInfo {
private String createBy;
private Date createTime;
//getter和setter方法省略
}
//修改角色实体类
public class SysRole {
// 其他原有属性
private CreateRoleInfo createRoleInfo;
//getter和setter方法省略
}
修改RoleMapper.xml中的roleMap配置:
<resultMap id="roleMap" type="SysRole">
<result property="id" column="role_id" />
<result property="roleName" column="role_name" />
<result property="enabled" column="enabled" />
<association property="createRoleInfo" javaType="CreateRoleInfo">
<result property="createBy" column="create_by" />
<result property="createTime" column="role_create_time" jdbcType="TIMESTAMP" />
</association>
</resultMap>
用户查询时collection标签关联到了该resultMap
3.3.3 实践练习
3.4 一对多映射中的嵌套查询
3.4.1 嵌套查询的层次
collection关联映射时,可以通过一个复杂的完整连接查询SQL关联,也可以通过多条简单SQL嵌套的方式实现。
- 在BRAC权限系统中,系统使用者的层次由下到上依次为权限、角色、用户,按照自下而上的过程,先执行下层查询获取指定角色所拥有的权限列表,然后执行上层查询获取指定用户所拥有的角色列表,这些都是<collection>标签的嵌套查询。
- 查询用户时可以可以collection关联查询用户的角色信息
- 查询用户角色时可以使用collection关联用户角色的权限信息
3.4.2 <collection>标签实现集合的嵌套查询
根据用户id获取用户及其对应的角色和权限信息的步骤如下:
- 在权限映射器PrivilegeMapper.xml中配置<resultMap>标签和对应查询方法,该方法实现的是根据角色id查询权限信息
<!--根据角色id获取该角色所拥有的权限-->
<select id="selectPrivilegesByRoleId" resultMap="privilegeMap">
select p.id privilege_id, privilege_name, privilege_url
from sys_privilege p join sys_role_privilege rp
on p.id=rp.privilege_id where rp.role_id=#{roleId}
</select>
在角色映射器RoleMapper.xml中配置<resultMap>标签和对应的查询方法(目的是使查询角色的时候关联查询出权限信息),代码如下:
<resultMap id="roleMap" type="SysRole">
<result property="id" column="role_id" />
<result property="roleName" column="role_name" />
<result property="enabled" column="enabled" />
<result property="createBy" column="create_by" />
<result property="createTime" column="role_create_time" jdbcType="TIMESTAMP" />
</resultMap>
<resultMap id="rolePrivilegeListMapSelect" type="SysRole" extends="roleMap">
<collection property="privilegeList" fetchType="lazy" column="{roleId=role_id}"
select="jack.mybatis.authority.mapper.PrivilegeMapper.selectPrivilegesByRoleId" />
</resultMap>
<!--根据用户id获取该用户所拥有的角色-->
<select id="selectRolesByUserId" resultMap="rolePrivilegeListMapSelect">
select r.id role_id, r.role_name, r.enabled, r.create_by, r.create_time
role_create_time from sys_role r join sys_user_role ur on r.id=ur.role_id
where ur.user_id=#{userId}
</select>
extends="roleMap" 关联权限的角色配置
在用户映射器UserMapper.xml中配置<resultMap>标签和对应的查询方法(目的是使查询用户的时候关联查询出角色信息),代码如下:
<resultMap id="userRoleListMapSelect" type="SysUser" extends="userMap">
<collection property="roleList" fetchType="lazy" column="{userId=id}"
select="jack.mybatis.authority.mapper.RoleMapper.selectRolesByUserId"/>
</resultMap>
<!--根据用户id获取用户信息以及用户的角色和权限信息-->
<select id="selectAllUserAndRolesSelectById" resultMap="userRoleListMapSelect">
select u.id, u.user_name, u.user_password, u.user_info, u.head_img,
u.create_time from sys_user u where u.id=#{id}
</select>
column="{userId=id} 用户关联角色的关联映射
在UserMapper接口中添加如下方法,代码如下:
// 根据用户id获取用户信息以及用户的角色和权限信息
SysUser selectAllUserAndRolesSelectById(Long id);
上面方法根据用户ID查询了一个用户实体对象,该用户实体对象中包含了该用户包含的角色集合List<SysRole>信息,而每个角色信息中则又包含了权限集合List<SysPrivilege >信息,都是 通过<collection>嵌套关联查询,而非一次性关联所有表查询出来的结果。
在测试selectAllUserAndRolesSelectById()方法
SqlSession sqlSession = getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
SysUser user = userMapper.selectAllUserAndRolesSelectById(1L);
System.out.println("用户名:"+user.getUserName());
for (SysRole role : user.getRoleList()) {
System.out.println("角色名:"+role.getRoleName());
for (SysPrivilege privilege : role.getPrivilegeList()) {
System.out.println("权限名:"+privilege.getPrivilegeName());
}
System.out.println("==========================");
}
3.4.3 实践练习
总结:
- MyBatis不仅能实现单表的结果映射查询,还能实现多表多层次的复杂映射。
- 多表关联查询映射时,可以在实体类中增加多表对应的字段或者关联对象属性,然后通过SQL语句中使用列别名完成自动化映射。
- MyBatis中使用<association>标签可以实现一对一、多对一时的实体对象关联映射配置,可以使用复杂连接查询一次性实现数据映射,也可以通过select和column属性实现多条SQL嵌套关联映射。
- MyBatis中使用<collection>标签实现一对多数据关联映射,此时实体类中需要声明List集合对应关联的多条数据记录,可以通过复杂连接查询一次性映射,也可以通过select和column属性实现多条SQL嵌套关联映射。
Java EE数据持久化框架 • 【第3章 MyBatis高级映射】的更多相关文章
- Java EE数据持久化框架笔记 • 【目录】
章节 内容 实践练习 Java EE数据持久化框架作业目录(作业笔记) 第1章 Java EE数据持久化框架笔记 • [第1章 MyBatis入门] 第2章 Java EE数据持久化框架笔记 • [第 ...
- Java EE数据持久化框架作业目录(作业笔记)
第1章 MyBatis入门>>> 1.1.4 在Eclipse中搭建MyBatis基本开发环境 1.2.5 使用MyBatis查询所有职员信息 1.3.3 获取id值为1的角色信息. ...
- Java EE数据持久化框架 • 【第5章 MyBatis代码生成器和缓存配置】
全部章节 >>>> 本章目录 5.1 配置MyBatis Generator 5.1.1 MyBatis Generator介绍 5.1.2 MyBatis Generat ...
- Java EE数据持久化框架 • 【第1章 MyBatis入门】
全部章节 >>>> 本章目录 1.1 初识MyBatis 1.1.1 持久化技术介绍 1.1.2 MyBatis简介 1.1.2 Mybatis优点 1.1.3 利用Mav ...
- Java EE数据持久化框架 • 【第2章 MyBatis实现DML操作】
全部章节 >>>> 本章目录 2.1 标签 2.1.1 标签简单应用 2.1.2 使用JDBC方式返回主键自增的值 2.1.3 使用标签返回普通主键的值 2.1.4 实践练 ...
- Java EE数据持久化框架 • 【第6章 MyBatis插件开发】
全部章节 >>>> 本章目录 6.1 MyBatis拦截器接口 6.1.1 MyBais拦截器接口介绍 6.1.2 MyBais拦截器签名介绍 6.1.3 实践练习 6.2 ...
- Java EE数据持久化框架 • 【第4章 MyBatis动态SQL】
全部章节 >>>> 本章目录 4.1 MyBatis动态标签 4.1.1 MyBatis动态标签介绍 4.1.2 < if >标签 4.1.3 update语 ...
- Java EE数据持久化框架mybatis练习——获取id值为1的角色信息。
实现要求: 获取id值为1的角色信息. 实现思路: 创建角色表sys_role所对应的实体类sysRole. package entity; public class SysRole { privat ...
- 第五章.MyBatis高级映射
5.1 新建数据库准备 CREATE TABLE `finacial_products` ( `product_id` ) NOT NULL AUTO_INCREMENT, `name` ) NO ...
随机推荐
- mysql报错max_connections错误
SELECT @@MAX_CONNECTIONS AS 'Max Connections';set GLOBAL max_connections=10000; show status like '%t ...
- Xcode功能快捷键
隐藏xcode command+h退出xcode command+q关闭窗口 command+w关闭所有窗口 command+option+w关闭当前项目 command+control+w关闭当前文 ...
- entfrm开源免费模块化无代码开发平台,开放生态为您创造更多的价值
entfrm开发平台6大特性,赋能快速开发,为您创造更多的价值: 1. 模块化 丰富的模块稳定的框架 后台极易上手 目前已包括系统管理.任务调度.运维监控.开发工具.消息系统.工作流引擎.内容管理等模 ...
- Spring Boot简单操作
目录 一.自定义异常页面 二.单元测试 三.多环境选择 四.读取主配置文件中的属性 五.读取List属性 一.自定义异常页面 对于404.405.500等异常状态,服务器会给出默认的异常页面,而这些 ...
- Delphi编译报错对照表
';' not allowed before 'ELSE' → ElSE前不允许有";" " clause not allowed in OLE automation s ...
- 【Fastjson】Fastjson反序列化由浅入深
Fastjson真-简-介 fastjson是由alibaba开发并维护的一个json工具,以其特有的算法,号称最快的json库 fastjson的使用 首先先创一个简单的测试类User public ...
- SWPUCTF 2019 pwn writeup
来做一下以前比赛的题目,下面两个题目都可以在buu复现(感谢赵总). SWPUCTF_2019_login 32位程序,考点是bss段上的格式化字符串.用惯onegadgets了,而对于32位程序来说 ...
- 利用模块加载回调函数修改PE导入表实现注入
最近整理PE文件相关代码的时候,想到如果能在PE刚刚读进内存的时候再去修改内存PE镜像,那不是比直接对PE文件进行操作隐秘多了么? PE文件在运行时会根据导入表来进行dll库的"动态链接&q ...
- JavaScript数组的push()等方法的使用
数组是值得有序集合.每个值在数组中有一个位置,用数字表示,叫做索引.JavaScript数组是无类型的:数组元素可以是任何类型,而且同一个数组中可以存在不同类型元素,甚至可以是对象或是其他数组,这就可 ...
- yum安装curl支持http2
yum 安装 安装 yum 源 rpm -ivh http://mirror.city-fan.org/ftp/contrib/yum-repo/city-fan.org-release-2-1.rh ...