Mybatis高级结果映射 [一对一, 一对多]

在关系型数据库中,经常要处理一对一、一对多的关系。例如在RBAC(Role-Based Access Control 基于角色的权限访问控制)权限系统中一个用户拥有多个角色,一个角色拥有多个权限这样复杂的嵌套关系。Mybatis可以轻松地解决这种复杂的关系。

数据库关系图

sys_user  -- 用户表

sys_role   -- 角色表

sys_user_role  -- 用户和角色关联表

sys_perimssion -- 权限表

sys_role_perimssion -- 角色和权限关联表

一对一映射

假设一个用户只能拥有一个角色, 那么它们之间的关系就是一对一的关系:

/**
 * 用户表实体
 */
@Data
public class User implements Serializable {

    private Long id;

    private String username;

    private String password;

    private Role role; // 一对一的关系

}

/**
 * 角色表实体
 */
@Data
public class Role implements Serializable {

    private Long id;

    private String name;

}

/**
 * 用户和角色关联表实体
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserRole implements Serializable {

    private Long userId;

    private Long roleId;

}

1. 自动映射实现一对一的映射:

    自动映射就是通过别名让mybatis自动将值匹配到对应的属性上,如 user_type 对应 userType, 除了这种之外,mybatis还支持多层嵌套的属性映射,例如 role.role_type 映射到 role.roleType上。Mybatis会在User中查找role属性,如果存在role属性就创建role对象,然后在role对象中继续查找roleType,将role_type的值绑定到role对象的roleType属性上。

<!-- UserMapper.xml -->

<select id="findUserWithRoleById" resultType="com.ex.sample.model.User">
    select 
        u.id,
        u.username,
        u.password,
        r.id "role.id",
        r.name "role.name",
        r.remark "role.remark" 
    from sys_user u 
    inner join sys_user_role ur on u.id = ur.user_id
    inner join sys_role r on ur.role_id = r.id
    where u.id = #{id}
</select>

2. resultMap实现一对一的映射:

在result中的column的值必须与sql查询结果的列名一致, 为了避免不同表中存在重名的列,对role的列都增加了"r_"前缀, sql中为其设置了别名。

<!-- UserMapper.xml -->

<resultMap id="baseResultMap" type="com.ex.sample.model.User">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <result property="password" column="password"/>
    
    <!-- Role 的属性 -->
    <result property="role.id" column="r_id"/>
    <result property="role.name" column="r_name"/>
    <result property="role.remark" column="r_remark"/>
</resultMap>

<select id="findUserWithRoleById" resultMap="baseResultMap">
    select 
        u.id,
        u.username,
        u.password,
        r.id r_id,
        r.name r_name,
        r.remark r_remark  
    from sys_user u 
    inner join sys_user_role ur on u.id = ur.user_id
    inner join sys_role r on ur.role_id = r.id
    where u.id = #{id}
</select>

3. resultMap的accociation标签实现一对一的映射:

在上面的代码的基础上,继续修改UserMapper.xml,增加如下代码:

<!-- 
    UserMapper.xml 

    resultMap:
        extends: 可以指定继承自某个resultMap, 那么对应的就拥有被继承者的所有字段

    association:
        property: 对应的实体类中的属性 (此处是User实体类中role属性)
        columnPrefix: 配置前缀后, 在子标签配置的result的column时可以省略前缀 (多表查询时, sql返回结果中可能存在相同的列, 默认在重复的列后加1, 但我们可以为列指定别名来区分)
        resultMap: 指定result映射,  也可以通过命名空间(e.g. com.ex.sample.mapper.RoleMapper)获得其他mapper.xml文件中的resultMap
-->

<resultMap id="userRoleMap" type="com.ex.sample.model.User" extends="baseResultMap">
    <association property="role" columnPrefix="r_" resultMap="com.ex.sample.mapper.RoleMapper.baseResultMap"/>
</resultMap>

<select id="findUserWithRole" resultMap="userRoleMap">
    select
        u.id,
        u.username,
        u.password,
        r.id r_id,
        r.name r_name,
        r.remark r_remark
    from sys_user u
    inner join sys_user_role ur on u.id = ur.user_id
    inner join sys_role r on ur.role_id = r.id
</select>

RoleMapper.xml 添加resultMap

<!-- RoleMapper.xml -->

<resultMap id="baseResultMap" type="com.ex.sample.model.Role">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="remark" column="remark"/>
</resultMap>

4. resultMap的association标签的嵌套查询:

嵌套查询是SQL通过多次查询得到的结果组合成一个对象,修改UserMapper.xml

<!-- 
    UserMapper.xml 
    
    association:
        column: 列名或别名, 将主查询中列的结果作为嵌套查询的参数,配置方式如 column={prop1=col1, prop2=col2}, prop1和prop2将作为嵌套查询的参数
        fetchType: 数据加载方式, 可选值为lazy和eager, 分别为延迟加载和积极加载, 这个配置会覆盖全局的lazyLoadingEnabled配置
-->

<resultMap id="userRoleMap" type="com.ex.sample.model.User" extends="baseResultMap">
    <association property="role" column="{id=role_id}" select="com.ex.sample.mapper.RoleMapper.findById" />
</resultMap>

<select id="findUserWithRoleById" resultMap="userRoleMap">
    select
        u.id,
        u.username,
        u.password,
        ur.role_id  
    from sys_user u
    inner join sys_user_role ur on u.id = ur.user_id
    where id = #{id}
</select>

RoleMapper.xml 增加findById

<!-- RoleMapper.xml -->

<resultMap id="baseResultMap" type="com.ex.sample.model.Role">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="remark" column="remark"/>
</resultMap>

<select id="findById" resultMap="baseResultMap">
    select * from sys_role where id = #{id}
</select>

一对多映射

一对多的关系, 主表中的一条数据会对应关联表中多条数据。如 一个用户拥有多个角色,每个角色又有多个权限的集合。

/**
 * 用户表实体
 */
@Data
public class User implements Serializable {

    private Long id;

    private String username;

    private String password;

    private List<Role> roles; // 一对多的关系

}

1. collection集合的结果映射

集合的结果映射就是指通过一次SQL查询将所有的结果查询出来,然后通过配置的结果映射,将数据映射到不同的对象中去。

<!-- UserMapper.xml -->

<resultMap id="userRoleMap" type="com.ericx.sboot.model.User" extends="baseResultMap">
    <collection property="roles" columnPrefix="r_" resultMap="com.ericx.sboot.mapper.RoleMapper.baseResultMap"/>
</resultMap>

<select id="findUserWithRoleById" resultMap="userRoleMap">
    select
        u.id,
        u.username,
        u.password,
        r.id r_id,
        r.name r_name,
        r.remark r_remark
    from sys_user u
    inner join sys_user_role ur on u.id = ur.user_id
    inner join sys_role r on ur.role_id = r.id
    where id = #{id}
</select>

2. collection集合的嵌套查询

对于角色又拥有多个权限, 所以这里存在嵌套的功能。

/**
 * 角色表实体
 */
@Data
public class Role {
    
    private Long id;
    
    private String name;

    private String remark;

    private List<Permission> permissions; // 一对多的关系
    
}


/**
 * 权限表实体
 */
@Data
public class Permission {
    
    private Long id;

    private String name;
    
}

/**
 * 角色和权限关联表实体
 */
@Data
public class RolePermission {
    
    private Long roleId;

    private Long permissionId;    

}
<!-- PermissionMapper.xml -->

<resultMap id="baseResultMap" type="com.ex.sample.model.Permission">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
</resultMap>

<select id="findPermissionsByRoleId" resultMap="baseResultMap">
    select 
        p.id,
        p.name,
    form sys_permission p 
    inner join sys_role_permission rp on p.id = rp.permission_id
    where rp.role_id = #{roleId}
</select>

RoleMapper.xml的配置和对应的查询方法,通过用户ID去查找对应的角色

<!-- RoleMapper.xml -->

<resultMap id="rolePermissionResultMap" extends="baseRequestMap" type="com.ex.sample.model.Role">
    <collection property="permissions" column="{roleId=id}" select="com.ex.sample.mapper.PermissionMapper.findPermissionsByRoleId"/>
</resultMap>

<select id="findRolesByUserId" resultMap="rolePermissionResultMap">
    select 
        r.id, 
        r.name, 
        r.remark  
    from sys_role r 
    inner join sys_user_role ur on r.id = ur.role_id
    where ur.user_id = #{userId}
</select>

UserMapper.xml 中只查询对应的用户

<!-- UserMapper.xml -->

<resultMap id="userResultMap" type="com.ex.sample.model.User" extends="baseResultMap">
    <collection property="roles" column="{userId=id}" select="com.ex.sample.mapper.RoleMapper.findRolesByUserId" fetchType="lazy"/>
</resultMap>

<select id="findUsers" resultMap="userResultMap">
    select * from sys_user
</select>

猜你喜欢

转载自blog.csdn.net/xiaeric/article/details/81772986
今日推荐