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 ...
随机推荐
- Android Handler 消息机制原理解析
前言 做过 Android 开发的童鞋都知道,不能在非主线程修改 UI 控件,因为 Android 规定只能在主线程中访问 UI ,如果在子线程中访问 UI ,那么程序就会抛出异常 android.v ...
- GO 总章
GO 学习资源 go 代理 GO 语言结构 GO 数字运算 GO 时间处理 GO 定时器 GO 异常处理 go recover让崩溃的程序继续执行 GO Exit Fatal panic GO 通过进 ...
- Linux学习 - 权限管理命令
一.chmod(change the permissions mode of a file) 1 功能 改变文件或目录权限 root 与 所有者 可进行此操作 2 语法 chmod [(ugoa) ...
- System.exit(-1)和return 的区别
对于只有一个单一方法的类或者系统来说是一样的,但是对于含有多个类和方法,且调用关系比较复杂时就不一样了. System.exit(-1)是指所有程序(方法,类等)停止,系统停止运行. return只是 ...
- List如何一边遍历,一边删除?
1.新手常犯的错误 可能很多新手(包括当年的我,哈哈)第一时间想到的写法是下面这样的: public static void main(String[] args) { List<String& ...
- Next_day()函数的用法
一.定义 NEXT_DAY(date,char) date参数为日期型, char:为1~7或Monday/Mon~Sunday/ 指定时间的下一个星期几(由char指定)所在的日期, c ...
- opencv学习(四)——轨迹栏作为调色板
轨迹栏作为调色板 在这里,我们将创建一个简单的应用程序,以显示指定的颜色.有一个显示颜色的窗口,以及三个用于指定B.G.R颜色的轨迹栏.滑动轨迹栏,并相应地更改窗口颜色.默认情况下,初始颜色将设置为黑 ...
- macOS Monterey 12.1 (21C52) 正式版 ISO、IPSW、PKG 下载
本站下载的 macOS Monterey 软件包,既可以拖拽到 Applications(应用程序)下直接安装,也可以制作启动 U 盘安装,或者在虚拟机中启动安装. 2021 年 12 月 14 日, ...
- 用法总结:NSArray,NSSet,NSDictionary
用法总结:NSArray,NSSet,NSDictionary Foundation framework中用于收集cocoa对象(NSObject对象)的三种集合分别是: NSArray 用于对象有序 ...
- <转>Java NIO API
Java NIO API详解 NIO API 主要集中在 java.nio 和它的 subpackages 中: java.nio 定义了 Buffer 及其数据类型相关的子类.其中被 java.ni ...