本章目录
3.1 一对一映射
3.1.1 自动化一对一映射
3.1.2 标签配置一对一映射
3.1.3 标签配置一对一映射
3.1.4 实践练习
3.2 一对一映射中的嵌套查询
3.2.1 标签嵌套查询属性
3.2.2 标签嵌套查询实现
3.2.3 实践练习
3.3 一对多映射
3.3.1 标签实现集合映射
3.3.2 标签和标签
3.3.3 实践练习
3.4 一对多映射中的嵌套查询
3.4.1 嵌套查询的层次
3.4.2 标签实现集合的嵌套查询
3.4.3 实践练习
总结:
实际开发中经常要处理一对一、一对多的关系。
权限系统中还存在着一个用户拥有多个角色、一个角色拥有多个权限这样复杂的嵌套关系,使用MyBatis的高级结果映射便可以轻松地处理这种一对一、一对多的关系。
自动映射就是查询时通过列的别名让MyBatis自动将值匹配到对应的实体字段上。
假设在RBAC权限系统中,一个用户只能拥有一个角色,如何通过MyBatis一次查 询出两个表的数据自动化映射至实体存储?
用户对应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执行速度很快时,建议使用关联的嵌套结果映射。
除了使用MyBatis的自动映射来处理一对一嵌套结果映射,还可以在Mapper.xml 映射文件中配置resultMap结果映射:
示例:
resultMap="userRoleMap" 注意:因为有两个create_time列,所以增加了role_create_time区分
观察发现,在配置resultMap标签时非常繁琐,既配置了SysUser的映射,又增加了包含的SysRole角色对象的映射,而上一章节已经配置了SysUser的映射,可以进行继承重用,如下:
extends=“userMap” 继承了另外一个resultMap
在
property="role" 用户中存储角色的实体属性名
columnPrefix="role_" 防止列名冲突,角色列添加前缀
javaType="SysRole" 角色的实体类型
使用
示例:
result结果映射时,会自动加上role_前缀和这里对应
既然SysUser映射时可以使用extends继承而简化,那么SysRole可以吗?
示例:
这里引用了角色RoleMapper中的映射定义
前面已经实现了通过一个复杂的连接查询语句,查询出用户及对应角色的数据,并且成功实现了结果的映射。除了通过复杂的单条SQL查询获取结果,还可以利用简单的多条SQL通过多次查询转换为我们需要的结果。
实现思路如下(两个SQL语句):
select:另一个映射查询的id,MyBatis会额外执行这个查询获取嵌套对象的结果。
column:列名(或别名),将主查询中列的结果作为嵌套查询的参数。
fetchType:数据加载方式,可选值为lazy(懒散)和eager(急切),分别为延迟加载和立即加载。
示例:
根据用户id获取用户和该用户所拥有的角色信息。
//RoleMapper接口中,根据角色ID查询角色信息
SysRole selectRoleById(Long id);
//UserMapper接口中,根据用户ID查询用户信息
SysUser selectUserAndRoleByIdSelect(Long id);
在一对多的关系中,主表的一条数据会对应关联表中的多条数据,因此一般查询时会查询出多个结果,所以在一的方向可以声明List集合来存储对应的多个数据。
在BRAC权限系统中,一个用户拥有多个角色(注意前面在使用
用户实体类中添加List集合用于存储该用户对应的多个角色,如下:
public SysUser{
// 其他原有属性省略
private List roleList; // 角色列表
//添加集合的setter和getter方法
// 其他原有字段的setter()方法和getter()方法
}
注意区分:前面是假设一个用户只能有一个角色时,使用的是角色对象
在UserMapper.xml定义id值为userRoleListMap的resultMap,如下:
标签用于配置一对多关系,对应的属性必须是对象中的集合类型,因此这里是roleList。 标签只是为了配置数据库字段和实体属性的映射关系,同时能存储一对多的数据结构肯定也能存储一对一关系,所以一对一是一对多的一种特例。
在映射器UserMapper新增方法selectAllUserAndRoles()方法,查询所有用户及其拥有的角色信息
List selectAllUserAndRoles();
在UserMapper.xml中新增selectAllUserAndRoles()方法,代码如下
测试selectAllUserAndRoles()方法,关键代码如下:
SqlSession sqlSession = getSqlSession();//获取SqlSession对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);//获取接口代理对象
List 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(“============结束============");
在配置角色时使用了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配置:
用户查询时collection标签关联到了该resultMap
collection关联映射时,可以通过一个复杂的完整连接查询SQL关联,也可以通过多条简单SQL嵌套的方式实现。
根据用户id获取用户及其对应的角色和权限信息的步骤如下:
在角色映射器RoleMapper.xml中配置
extends="roleMap" 关联权限的角色配置
在用户映射器UserMapper.xml中配置
column="{userId=id} 用户关联角色的关联映射
在UserMapper接口中添加如下方法,代码如下:
// 根据用户id获取用户信息以及用户的角色和权限信息
SysUser selectAllUserAndRolesSelectById(Long id);
上面方法根据用户ID查询了一个用户实体对象,该用户实体对象中包含了该用户包含的角色集合List
信息,而每个角色信息中则又包含了权限集合List 信息,都是 通过 嵌套关联查询,而非一次性关联所有表查询出来的结果。
在测试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("==========================");
}