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 ...
随机推荐
- Ubuntu下STL源码文件路径+VS2010下查看STL源码
Ubuntu版本信息 然后STL源码位置就在 /usr/include/c++/7/bits /usr/include/c++/7.4.9/bits 这两个文件下都有 然后我日常写程序用的Window ...
- Oracle参数文件—pfile与spfile
oracle的参数文件:pfile和spfile 1.pfile和spfile Oracle中的参数文件是一个包含一系列参数以及参数对应值的操作系统文件.它们是在数据库实例启动时候加载的, ...
- Oracle中dbms_random包详解
Oracle之DBMS_RANDOM包详解参考自:https://www.cnblogs.com/ivictor/p/4476031.html https://www.cnblogs.com/shen ...
- github单独下载某一个文件夹
可以借助svn工具进行下载,实现只下载repo下的指定文件夹内容 背景 需要下载这个文件夹下所有内容https://github.com/rabbitmq/rabbitmq-tutorials/tre ...
- win10安装两台mysql-5.7.31实例
1. 下载 mysql5.7.31 压缩包: (1)百度云下载: 链接:https://pan.baidu.com/s/1jgxfvIYzg8B8ahxU9pF6lg 提取码:fiid (2)官网下载 ...
- 实现nfs持久挂载+autofs自动挂载
实验环境: 两台主机 node4:192.168.37.44 NFS服务器 node2:192.168.37.22 客户端 在nfs服务器,先安装nfs和rpcbind [root@node4 fen ...
- vue实现input输入框的模糊查询
最近在用uni-app做一个项目,使用的框架还是vue,想了好久才做出来 . HTML代码部分 <input type="text" focus class="s ...
- Snort 入侵检测系统
Snort 入侵检测系统 一.实验目的 1.掌握snort IDS工作原理 2.应用snort 三种方式工作 二.实验环境 系统环境:Windows环境, kali环境 三.实验原理 1.snort ...
- Linux服务器被黑 排查思路
目录 一.为何会被入侵? 二.排查 入侵排查 检查是否还存在被登陆可能 计划任务 被修改的文件 筛选日志 日志恢复 找到异常进程-1 找到异常进程-2 找到异常进程-3 找到异常进程-4 三.总结 一 ...
- TSN 时间敏感网络:缘起 (TSN历史与现状)
前言 随着工业物联网(IIoT)的兴起和工业4.0的提出,越来越多的设计师.工程师和最终用户关注时间敏感网络(Time-Sensitive Networking,下简称为TSN).TSN为以太网提供确 ...