MyBatis入门-高级结果映射

MyBatis高级结果映射

本篇文章主要介绍

  • association和collection标签的用法
  • 嵌套结果映射和嵌套查询映射

介绍-一对一映射

一对一映射因为不需要考虑是否存在重复数据,使用简单,可以直接使用MyBatis自动映射。

实践-自动映射

要求一个用户对应一个角色,类定义如下

public class SysUser implements Serializable {
    private static final long serialVersionUID = 854674092994608168L;
   .....
    private SysRole role;
    ......
}

自动映射就是MyBatis将查询的结果通过别名映射到返回的对象上。复杂的属性映射如:将role.role_name映射给role.roleName上;MyBatis会先查找role属性,如果存在role属性就创建role对象,然后再role对象中继续查找roleName,将role_name的值绑定到role对象的roleName属性上。

mapper接口

/**
     * 根据用户id获取用户信息和用户的角色信息
     * @param id 用户id
     * @return 用户和角色信息
     */
    SysUser selectUserAndRoleById(Long id);

xml,注意其中role属性的roleName字段,我用的是数据库字段,role_name;其他字段都是驼峰规则,直接对应role对象的属性字段

<select id="selectUserAndRoleById" resultType="tk.mybatis.simple.model.SysUser">
        select
        u.id,
        u.user_name,
        u.user_password,
        u.user_email,
        u.user_info,
        u.head_img,
        u.create_time,
        sr.id        "role.id",
        sr.role_name "role.role_name",
        sr.enabled   "role.enabled",
        sr.create_by "role.createBy",
        sr.create_time "role.createTime"
        from sys_user u
        inner join sys_user_role sur on u.id = sur.user_id
        inner join sys_role sr on sur.role_id = sr.id
        where u.id =#{id}
    </select>

测试方法:

public void selectUserAndRoleById() {
        SqlSession sqlSession = getSqlSession();
        try {
            SysUserMapper mapper = sqlSession.getMapper(SysUserMapper.class);

            SysUser sysUser = mapper.selectUserAndRoleById(1L);
            Assert.assertNotNull(sysUser);
            Assert.assertNotNull(sysUser.getRole());
            LOGGER.info("用户信息:{}",sysUser.toString());
        } finally {
            sqlSession.close();
        }
    }

测试结果:能够发现role对象的roleName属性也有值,表示mybatis查询的属性列,会将下划线转化为驼峰规则。因为SysRole类中也有SysUser对象,发现最终role对象中的user属性值为空。

==>  Preparing: select u.id, u.user_name, u.user_password, u.user_email, u.user_info, u.head_img, u.create_time, sr.id "role.id", sr.role_name "role.role_name", sr.enabled "role.enabled", sr.create_by "role.createBy", sr.create_time "role.createTime" from sys_user u inner join sys_user_role sur on u.id = sur.user_id inner join sys_role sr on sur.role_id = sr.id where u.id =? 
==> Parameters: 1(Long)
<==    Columns: id, user_name, user_password, user_email, user_info, head_img, create_time, role.id, role.role_name, role.enabled, role.createBy, role.createTime
<==        Row: 1, admin, 123456, admin@mybatis.tk, <<BLOB>>, <<BLOB>>, 2021-01-28 21:57:32.0, 1, 管理员, 1, 1, 2021-01-28 21:58:32.0
<==      Total: 1
用户信息:SysUser{id=1, userName='admin', userPassword='123456', userEmail='admin@mybatis.tk', userInfo='管理员', headImg=null, createTime=Thu Jan 28 21:57:32 CST 2021, role=SysRole{id=1, roleName='管理员', enabled=1, createBy=1, createTime=Thu Jan 28 21:58:32 CST 2021, user=null}}

总结:这种通过一次查询将结果映射导不同对象的方式,被称为嵌套结果查询。

这种方式的好处是减少数据库查询次数,减轻数据库的压力,缺点是要写复杂的sql;由于要在应用服务器上将结果映射到不同类上,会增加服务器的压力。

实践-resultMap配置

该方法使用resultMap进行列名与对象属性映射,需要保证列名不可重复。

mapper,接口返回和上面的自动映射实践一致

   /**
     * 根据用户id获取用户信息和用户的角色信息
     * @param id 用户id
     * @return 用户和角色信息
     */
    SysUser selectUserAndRoleById2(Long id);

xml,userRoleMap中对role对象关联的部分列名做了别名映射,因为resulMap中列名不能重复。resultMap中result标签与sql的映射关系可以看为:sql查询列名->result标签column属性名->result标签property属性->resultType类属性。

<resultMap id="userRoleMap" type="tk.mybatis.simple.model.SysUser">
        <!--@Table sys_user-->
        <result property="id" column="id" jdbcType="INTEGER"/>
        <result property="userName" column="user_name" jdbcType="VARCHAR"/>
        <result property="userPassword" column="user_password" jdbcType="VARCHAR"/>
        <result property="userEmail" column="user_email" jdbcType="VARCHAR"/>
        <result property="userInfo" column="user_info" jdbcType="VARCHAR"/>
        <result property="headImg" column="head_img" jdbcType="OTHER"/>
        <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
        <!--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_password,
        u.user_email,
        u.user_info,
        u.head_img,
        u.create_time,
        sr.id role_id,
        sr.role_name,
        sr.enabled,
        sr.create_by ,
        sr.create_time role_create_time
        from sys_user u
        inner join sys_user_role sur on u.id = sur.user_id
        inner join sys_role sr on sur.role_id = sr.id
        where u.id =#{id}
    </select>

测试方法:和上面自动映射的测试方法一致,只修改查询的方法为selectUserAndRoleById2即可。

测试结果:也可以查询成功。

==>  Preparing: select u.id, u.user_name, u.user_password, u.user_email, u.user_info, u.head_img, u.create_time, sr.id role_id, sr.role_name, sr.enabled, sr.create_by , sr.create_time role_create_time from sys_user u inner join sys_user_role sur on u.id = sur.user_id inner join sys_role sr on sur.role_id = sr.id where u.id =? 
==> Parameters: 1(Long)
<==    Columns: id, user_name, user_password, user_email, user_info, head_img, create_time, role_id, role_name, enabled, create_by, role_create_time
<==        Row: 1, admin, 123456, admin@mybatis.tk, <<BLOB>>, <<BLOB>>, 2021-01-28 21:57:32.0, 1, 管理员, 1, 1, 2021-01-28 21:58:32.0
<==      Total: 1
用户信息:SysUser{id=1, userName='admin', userPassword='123456', userEmail='admin@mybatis.tk', userInfo='管理员', headImg=null, createTime=Thu Jan 28 21:57:32 CST 2021, role=SysRole{id=1, roleName='管理员', enabled=1, createBy=1, createTime=Thu Jan 28 21:58:32 CST 2021, user=null}}

总结:这个userRoleMap使用起来十分繁琐。可以使用resultMap的继承来简化userRoleMap。

使用如下的resultMap写法,继承基础resutlMap,extends="BaseResultMap",也可以得到上面相同的结果。

 <resultMap id="userRoleMap" extends="BaseResultMap" type="tk.mybatis.simple.model.SysUser">
        <!--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>

实践-association配置

association标签用于和一个复杂的类型进行关联,即用于一对一的关联配置。

mapper接口

  /**
     * 根据用户id获取用户信息和用户的角色信息
     * @param id 用户id
     * @return 用户和角色信息
     */
    SysUser selectUserAndRoleById3(Long id);

xml,使用association标签来映射role属性,其中property就是SysUser中的SysRole属性role。columnPrefix表示SysRole类中属性对应的数据库列都有一个前缀。association标签内部的result标签中property指的就是SysRole类中的属性。MyBatis在映射结果时会自动使用前缀和column值得组合去sql查询的结果取值。

<resultMap id="userRoleMap3" extends="BaseResultMap" type="tk.mybatis.simple.model.SysUser">
        <association property="role" javaType="tk.mybatis.simple.model.SysRole" columnPrefix="role_">
            <!--role相关属性-->
            <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>

<select id="selectUserAndRoleById3" resultMap="userRoleMap3">
        select
        u.id,
        u.user_name,
        u.user_password,
        u.user_email,
        u.user_info,
        u.head_img,
        u.create_time,
        sr.id role_id,
        sr.role_name role_role_name,
        sr.enabled role_enabled,
        sr.create_by role_create_by,
        sr.create_time role_create_time
        from sys_user u
        inner join sys_user_role sur on u.id = sur.user_id
        inner join sys_role sr on sur.role_id = sr.id
        where u.id =#{id}
    </select>

association标签包含以下属性:

  • property:对应被配置一对一属性对应实体类中的属性名,必填
  • javaType:对应被配置一对一属性对应的Java类型
  • resultMap:可以直接使用现有的resultMap
  • columnPrefix:查询的列的前缀,配置前缀后,在子标签配置result的column时可以省略前缀

association标签中的resultMap可以使用现有的配置,例如上面的属性类型是SysRole,我们就可以使用SysRole对应的baseMap。如下的配置,也可以成功运行。

<resultMap id="userRoleMap3" extends="BaseResultMap" type="tk.mybatis.simple.model.SysUser">
        <association property="role" javaType="tk.mybatis.simple.model.SysRole" columnPrefix="role_" resultMap="tk.mybatis.simple.mapper.SysRoleMapper.BaseResultMap">
        </association>
    </resultMap>

实践-association嵌套查询

除了上面的关联嵌套结果映射查询,还有一种利用简单的sql通过多次查询转化为我们需要的结果,这种方式与根据业务逻辑手动执行多次sql的方式类似。

association标签的嵌套查询常用的属性如下:

  • select:查询标签的id,MyBatis会额外执行这个查询获取嵌套对象的结果
  • column:列名,将主查询种的列的结果作为嵌套查询的参数,配置方式如column={prop1=col1,prop2=col2},prop1和pro2将作为嵌套查询的参数
  • fetchType:数据加载方式,可选值为lazy和eager,这个配置会覆盖全局的lazyLoadingEnabled配置

先在SysRoleMapper中添加一个通过主键查数据的方法

    /**
     * 通过主键查角色
     * @param id 主键
     * @return 角色
     */
    SysRole queryById(Long id);

在SysUserMapper中添加一个接口

    /**
     * 通过association嵌套查询获取用户信息
     * @param id 用户主键
     * @return 用户和角色信息
     */
    SysUser selectUserAndRoleByIdSelect(Long id);

xml,对resultMap中的association标签添加一个column属性,该属性中的{id=role_id}表示select查询标签的查询参数id取值来自当前查询返回的role_id值。所以sql中必须要返回一个名为role_id的字段。

即column属性中等号左边的表示select标签的参数,等号右边的表示来源的值。该参数也可以为多个,如{id=role_id,name=role_name}

 <resultMap id="userRoleMapSelect" extends="BaseResultMap" type="tk.mybatis.simple.model.SysUser">
        <association property="role" javaType="tk.mybatis.simple.model.SysRole"
                     column="{id=role_id}" select="tk.mybatis.simple.mapper.SysRoleMapper.queryById">
        </association>
    </resultMap>

    <select id="selectUserAndRoleByIdSelect" resultMap="userRoleMapSelect">
        select
        u.id,
        u.user_name,
        u.user_password,
        u.user_email,
        u.user_info,
        u.head_img,
        u.create_time,
        sur.role_id
        from sys_user u
        inner join sys_user_role sur on u.id = sur.user_id
        where u.id =#{id}
    </select>

测试方法和上面的都是类似的,测试结果如下。执行了两次sql。

==>  Preparing: select u.id, u.user_name, u.user_password, u.user_email, u.user_info, u.head_img, u.create_time, sur.role_id from sys_user u inner join sys_user_role sur on u.id = sur.user_id where u.id =? 
==> Parameters: 1(Long)
<==    Columns: id, user_name, user_password, user_email, user_info, head_img, create_time, role_id
<==        Row: 1, admin, 123456, admin@mybatis.tk, <<BLOB>>, <<BLOB>>, 2021-01-28 21:57:32.0, 1
====>  Preparing: SELECT r.id, r.role_name, r.enabled, r.create_by, r.create_time from sys_role r where r.id = ? 
====> Parameters: 1(Long)
<====    Columns: id, role_name, enabled, create_by, create_time
<====        Row: 1, 管理员, 1, 1, 2021-01-28 21:58:32.0
<====      Total: 1
<==      Total: 1
用户信息:SysUser{id=1, userName='admin', userPassword='123456', userEmail='admin@mybatis.tk', userInfo='管理员', headImg=null, createTime=Thu Jan 28 21:57:32 CST 2021, role=SysRole{id=1, roleName='管理员', enabled=1, createBy=1, createTime=Thu Jan 28 21:58:32 CST 2021, user=null}}

上面这种嵌套查询,想要在需要role属性的时候再去做查询,这就需要添加association的加载标签和修改mybatis-config.xml中的aggressiveLazyLoading值。

<resultMap id="userRoleMapSelect" extends="BaseResultMap" type="tk.mybatis.simple.model.SysUser">
        <association property="role" javaType="tk.mybatis.simple.model.SysRole"
                     column="{id=role_id}" select="tk.mybatis.simple.mapper.SysRoleMapper.queryById" fetchType="lazy">
        </association>
    </resultMap>

<settings>
        <setting name="logImpl" value="LOG4J"/>
<!--        开启数据库列驼峰转化-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

aggressiveLazyLoading属性的含义是:当该参数设置为true时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载,反之,每种属性都将按需加载。

当我们需要在出发某些方法时,将所有的数据加载进来,aggressiveLazyLoading值为false时,我们可以使用lazyLoadTriggerMethods,该参数的含义是,当调用配置中的方法时,加载全部的延迟加载数据。

  <settings>
        <setting name="logImpl" value="LOG4J"/>
<!--        开启数据库列驼峰转化-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

介绍-一对多映射

实践-collection嵌套结果映射

和association类似,集合嵌套结果映射就是指通过一次sql查询所有结果,然后通过配置的结果映射,将数据映射到不同的对象中。

一个用户有多个角色,一个角色有多种权限。

SysUser2定义,和SysUser的区别在于存储用户角色用List集合存储。

 private List<SysRole> roleList;

Mapper接口

/**
* 查询所有用户对应的角色信息
* @return 用户信息
*/
List<SysUser2> selectAllUserAndRoles();

xml写法

1.第一种自定义map,需要注意collection标签中的ofType,ofType值表示property值list存储的类型。

    <resultMap id="userRoleListMap" type="tk.mybatis.simple.model.SysUser2">
        <!--@Table sys_user-->
        <id property="id" column="id" jdbcType="INTEGER"/>
        <result property="userName" column="user_name" jdbcType="VARCHAR"/>
        <result property="userPassword" column="user_password" jdbcType="VARCHAR"/>
        <result property="userEmail" column="user_email" jdbcType="VARCHAR"/>
        <result property="userInfo" column="user_info" jdbcType="VARCHAR"/>
        <result property="headImg" column="head_img" jdbcType="OTHER"/>
        <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
        <collection property="roleList"  ofType="tk.mybatis.simple.model.SysRole"
                    columnPrefix="role_">
            <result property="id" column="id" jdbcType="INTEGER"/>
            <result property="roleName" column="role_name" jdbcType="VARCHAR"/>
            <result property="enabled" column="enabled" jdbcType="INTEGER"/>
            <result property="createBy" column="create_by" jdbcType="INTEGER"/>
            <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
        </collection>
    </resultMap>

2.第二种map,和上面的区别在于将SysUser2的字段用extends来继承,roleList里的字段在collection标签中用resultMap来存储。

<resultMap id="userRoleListMap2" extends="BaseResultMap" type="tk.mybatis.simple.model.SysUser2">
        <collection property="roleList"
                    columnPrefix="role_" resultMap="tk.mybatis.simple.mapper.SysRoleMapper.BaseResultMap">
        </collection>
    </resultMap>

查询的sql

 <select id="selectAllUserAndRoles" resultMap="userRoleListMap">
        select
        u.id,
        u.user_name,
        u.user_password,
        u.user_email,
        u.user_info,
        u.head_img,
        u.create_time,
        sr.id role_id,
        sr.role_name role_role_name,
        sr.enabled role_enabled,
        sr.create_by role_create_by,
        sr.create_time role_create_time
        from sys_user u
        inner join sys_user_role sur on u.id = sur.user_id
        inner join sys_role sr on sur.role_id = sr.id
    </select>

测试方法

    public void selectAllUserAndRoles() {
        SqlSession sqlSession = getSqlSession();
        try {
            SysUserMapper mapper = sqlSession.getMapper(SysUserMapper.class);

            List<SysUser2> sysUserList = mapper.selectAllUserAndRoles();
            for (int i = 0; i < sysUserList.size(); i++) {
                LOGGER.info("第{}个{}",i+1,sysUserList.get(i));
            }
        } finally {
            sqlSession.close();
        }
    }

测试结果,用上面两种resultMap执行结果一致

==>  Preparing: select u.id, u.user_name, u.user_password, u.user_email, u.user_info, u.head_img, u.create_time, sr.id role_id, sr.role_name role_role_name, sr.enabled role_enabled, sr.create_by role_create_by, sr.create_time role_create_time from sys_user u inner join sys_user_role sur on u.id = sur.user_id inner join sys_role sr on sur.role_id = sr.id 
==> Parameters: 
<==    Columns: id, user_name, user_password, user_email, user_info, head_img, create_time, role_id, role_role_name, role_enabled, role_create_by, role_create_time
<==        Row: 1, admin, 123456, admin@mybatis.tk, <<BLOB>>, <<BLOB>>, 2021-01-28 21:57:32.0, 1, 管理员, 1, 1, 2021-01-28 21:58:32.0
<==        Row: 1001, test, 123456, test@mybatis.tk, <<BLOB>>, <<BLOB>>, 2021-01-28 21:58:32.0, 2, 普通用户, 1, 1, 2021-01-28 21:58:32.0
<==        Row: 1, admin, 123456, admin@mybatis.tk, <<BLOB>>, <<BLOB>>, 2021-01-28 21:57:32.0, 2, 普通用户, 1, 1, 2021-01-28 21:58:32.0
<==      Total: 31个SysUser2{id=1, userName='admin', userPassword='123456', userEmail='admin@mybatis.tk', userInfo='管理员', headImg=null, createTime=Thu Jan 28 21:57:32 CST 2021, roleList=[SysRole{id=1, roleName='管理员', enabled=1, createBy=1, createTime=Thu Jan 28 21:58:32 CST 2021, user=null}, SysRole{id=2, roleName='普通用户', enabled=1, createBy=1, createTime=Thu Jan 28 21:58:32 CST 2021, user=null}]}
第2个SysUser2{id=1001, userName='test', userPassword='123456', userEmail='test@mybatis.tk', userInfo='测试用户', headImg=null, createTime=Thu Jan 28 21:58:32 CST 2021, roleList=[SysRole{id=2, roleName='普通用户', enabled=1, createBy=1, createTime=Thu Jan 28 21:58:32 CST 2021, user=null}]}

总结:sql查出来的结果有3条,经过MyBatis对collection数据处理后,变成了两条。

MyBatis在处理结果的时候,会判断结果是否相同,如果是相同的结果,则只会保留第一个结果。MyBatis如何判断结果是否相同呢?

  1. 最简单的情况就是在外层映射配置文件中至少有一个id标签,即<id property="id" column="id" jdbcType="INTEGER"/>。id标签配置的字段为表的主键,因为MyBatis的resultMap只用于配置结果如何映射,并不知道这个表具体如何。id的唯一作用就是在嵌套的映射配置时判断数据是否相同,当配置id标签时,MyBatis只需要逐条比较所有数据中id标签配置的字段值是否相同即可。在配置嵌套结果查询时,配置id标签可以提高处理效率。

为了验证上面id标签的作用,我们修改resultMap的值,将id标签的值换为userPassword,执行测试方法。

        <id property="userPassword" column="user_password" jdbcType="VARCHAR"/>

测试结果:三条记录的密码都是123456,按照id标签配置,这三条数据会合并成一条数据。发现只保留了查询的第一条admin用户,角色信息只有两条,因为普通用户这个角色重复了。

==>  Preparing: select u.id, u.user_name, u.user_password, u.user_email, u.user_info, u.head_img, u.create_time, sr.id role_id, sr.role_name role_role_name, sr.enabled role_enabled, sr.create_by role_create_by, sr.create_time role_create_time from sys_user u inner join sys_user_role sur on u.id = sur.user_id inner join sys_role sr on sur.role_id = sr.id 
==> Parameters: 
<==    Columns: id, user_name, user_password, user_email, user_info, head_img, create_time, role_id, role_role_name, role_enabled, role_create_by, role_create_time
<==        Row: 1, admin, 123456, admin@mybatis.tk, <<BLOB>>, <<BLOB>>, 2021-01-28 21:57:32.0, 1, 管理员, 1, 1, 2021-01-28 21:58:32.0
<==        Row: 1001, test, 123456, test@mybatis.tk, <<BLOB>>, <<BLOB>>, 2021-01-28 21:58:32.0, 2, 普通用户, 1, 1, 2021-01-28 21:58:32.0
<==        Row: 1, admin, 123456, admin@mybatis.tk, <<BLOB>>, <<BLOB>>, 2021-01-28 21:57:32.0, 2, 普通用户, 1, 1, 2021-01-28 21:58:32.0
<==      Total: 31个SysUser2{id=1, userName='admin', userPassword='123456', userEmail='admin@mybatis.tk', userInfo='管理员', headImg=null, createTime=Thu Jan 28 21:57:32 CST 2021, roleList=[SysRole{id=1, roleName='管理员', enabled=1, createBy=1, createTime=Thu Jan 28 21:58:32 CST 2021, user=null}, SysRole{id=2, roleName='普通用户', enabled=1, createBy=1, createTime=Thu Jan 28 21:58:32 CST 2021, user=null}]}

注意:如果没有配置id标签,MyBatis就会把resultMap中配置的所有result字段进行比较,如果所有的字段值相同就合并。在嵌套结果配置id属性时,如果查询语句中没有查询id属性配置的列,就会导致id对应的值为null。这种情况下,所有值的id都相同,因此会使嵌套的集合中只有一条数据。所以在配置id列时,查询语句中必须包含该列。

现在需要将一个角色对应多个权限,所以需要在角色的resultmap中配置权限字段。

SysPrivilege.xml,指定权限类与表对应的基础BaseResultMap

    <resultMap id="BaseResultMap" type="tk.mybatis.simple.model.SysPrivilege">
        <!--@Table sys_privilege-->
        <id property="id" column="id" jdbcType="INTEGER"/>
        <result property="privilegeName" column="privilege_name" jdbcType="VARCHAR"/>
        <result property="privilegeUrl" column="privilege_url" jdbcType="VARCHAR"/>
    </resultMap>

SysRole类添加存储角色包含的权限列表

private List<SysPrivilege> privilegeList;

SysRoleMapper.xml,一个角色对应多个权限,配置collection,并在collection标签中添加resultMap属性绑定SysPrivilegeMapper中的BaseResultMap的值

    <resultMap id="rolePrivilegeListMap" extends="BaseResultMap"
               type="tk.mybatis.simple.model.SysRole">
        <collection property="privilegeList" columnPrefix="privilege_"
                    resultMap="tk.mybatis.simple.mapper.SysPrivilegeMapper.BaseResultMap">
        </collection>
    </resultMap>

SysUserMapper.xml,将用户一对多角色配置的collection中resultMap指定为上面配置在SysRoleMapper.xml中的rolePrivilegeListMap

<resultMap id="userRoleListMap3" extends="BaseResultMap" type="tk.mybatis.simple.model.SysUser2">
        <collection property="roleList"
                    columnPrefix="role_" resultMap="tk.mybatis.simple.mapper.SysRoleMapper.rolePrivilegeListMap">
        </collection>
    </resultMap>

最终sql的写法:

select u.id,
               u.user_name,
               u.user_password,
               u.user_email,
               u.user_info,
               u.head_img,
               u.create_time,
               sr.id          role_id,
               sr.role_name   role_role_name,
               sr.enabled     role_enabled,
               sr.create_by   role_create_by,
               sr.create_time role_create_time,
               sp.id role_privilege_id,
               sp.privilege_name role_privilege_privilege_name,
               sp.privilege_url role_privilege_privilege_url
        from sys_user u
                 inner join sys_user_role sur on u.id = sur.user_id
                 inner join sys_role sr on sur.role_id = sr.id
                 inner join sys_role_privilege srp on sur.role_id = srp.role_id
                 inner join sys_privilege sp on srp.privilege_id = sp.id

需要注意的是,我们嵌套使用了collection标签,每一个collection标签上都有一个column_prefix字段。SysUserMapper中第一层嵌套是role_,所以在绑定的resultMap(rolePrivilegeListMap)的所有查询出的字段都要加上role_;rolePrivilegeListMap中的resultMap也有一层嵌套privilege_,所以绑定的resultMap(SysPrivilegeMapper.BaseResultMap)的所有查询出来的字段都要加上privilege_;前缀的叠加顺序是从外到内的。

测试输出结果:

==>  Preparing: select u.id, u.user_name, u.user_password, u.user_email, u.user_info, u.head_img, u.create_time, sr.id role_id, sr.role_name role_role_name, sr.enabled role_enabled, sr.create_by role_create_by, sr.create_time role_create_time, sp.id role_privilege_id, sp.privilege_name role_privilege_privilege_name, sp.privilege_url role_privilege_privilege_url from sys_user u inner join sys_user_role sur on u.id = sur.user_id inner join sys_role sr on sur.role_id = sr.id inner join sys_role_privilege srp on sur.role_id = srp.role_id inner join sys_privilege sp on srp.privilege_id = sp.id 
==> Parameters: 
<==    Columns: id, user_name, user_password, user_email, user_info, head_img, create_time, role_id, role_role_name, role_enabled, role_create_by, role_create_time, role_privilege_id, role_privilege_privilege_name, role_privilege_privilege_url
<==        Row: 1, admin, 123456, admin@mybatis.tk, <<BLOB>>, <<BLOB>>, 2021-01-28 21:57:32.0, 1, 管理员, 1, 1, 2021-01-28 21:58:32.0, 1, 用户管理, /users
<==        Row: 1, admin, 123456, admin@mybatis.tk, <<BLOB>>, <<BLOB>>, 2021-01-28 21:57:32.0, 1, 管理员, 1, 1, 2021-01-28 21:58:32.0, 3, 系统日志, /logs
<==        Row: 1, admin, 123456, admin@mybatis.tk, <<BLOB>>, <<BLOB>>, 2021-01-28 21:57:32.0, 1, 管理员, 1, 1, 2021-01-28 21:58:32.0, 2, 角色管理, /roles
<==        Row: 1001, test, 123456, test@mybatis.tk, <<BLOB>>, <<BLOB>>, 2021-01-28 21:58:32.0, 2, 普通用户, 1, 1, 2021-01-28 21:58:32.0, 4, 人员维护, /persons
<==        Row: 1001, test, 123456, test@mybatis.tk, <<BLOB>>, <<BLOB>>, 2021-01-28 21:58:32.0, 2, 普通用户, 1, 1, 2021-01-28 21:58:32.0, 5, 单位维护, /companies
<==        Row: 1, admin, 123456, admin@mybatis.tk, <<BLOB>>, <<BLOB>>, 2021-01-28 21:57:32.0, 2, 普通用户, 1, 1, 2021-01-28 21:58:32.0, 4, 人员维护, /persons
<==        Row: 1, admin, 123456, admin@mybatis.tk, <<BLOB>>, <<BLOB>>, 2021-01-28 21:57:32.0, 2, 普通用户, 1, 1, 2021-01-28 21:58:32.0, 5, 单位维护, /companies
<==      Total: 71个SysUser2{id=1, userName='admin', userPassword='123456', userEmail='admin@mybatis.tk', userInfo='管理员', headImg=null, createTime=Thu Jan 28 21:57:32 CST 2021, roleList=[SysRole{id=1, roleName='管理员', enabled=1, createBy=1, createTime=Thu Jan 28 21:58:32 CST 2021, user=null, privilegeList=[SysPrivilege{id=1, privilegeName='用户管理', privilegeUrl='/users'}, SysPrivilege{id=3, privilegeName='系统日志', privilegeUrl='/logs'}, SysPrivilege{id=2, privilegeName='角色管理', privilegeUrl='/roles'}]}, SysRole{id=2, roleName='普通用户', enabled=1, createBy=1, createTime=Thu Jan 28 21:58:32 CST 2021, user=null, privilegeList=[SysPrivilege{id=4, privilegeName='人员维护', privilegeUrl='/persons'}, SysPrivilege{id=5, privilegeName='单位维护', privilegeUrl='/companies'}]}]}
第2个SysUser2{id=1001, userName='test', userPassword='123456', userEmail='test@mybatis.tk', userInfo='测试用户', headImg=null, createTime=Thu Jan 28 21:58:32 CST 2021, roleList=[SysRole{id=2, roleName='普通用户', enabled=1, createBy=1, createTime=Thu Jan 28 21:58:32 CST 2021, user=null, privilegeList=[SysPrivilege{id=4, privilegeName='人员维护', privilegeUrl='/persons'}, SysPrivilege{id=5, privilegeName='单位维护', privilegeUrl='/companies'}]}]}

总结:一个复杂的映射是由一个基本的映射配置组成。通常情况下,如果要配置一个复杂的映射,要从基础映射开始配置,每增加一些配置就进行对应的测试。

实践-collection嵌套select查询

一个用户对应多个角色,一个角色对应多个权限。

1.先进行一个角色查权限

PrivilegeMapper中添加一个通过角色id查询权限的方法

    /**
     * 通过角色id查询权限信息
     * @param roleId 角色id
     * @return 权限信息
     */
    List<SysPrivilege> selectByRoleId(Long roleId);

PrivilegeMapper.xml

    <select id="selectByRoleId" resultMap="BaseResultMap">
        select p.id, p.privilege_url, p.privilege_name
        from sys_privilege p
        inner join sys_role_privilege srp on p.id = srp.privilege_id
        where srp.role_id = #{roleId}
    </select>

2.角色中定义一个resultMap来利用上面的通过角色id查询权限的select

SysRoleMapper.xml,其中collection标签中:property表示属性名,fetchType表示子查询加载方式,column中{}表示可以有多个字段对应的条件,其中=左边是子查询的参数,右边是当前查询列字段;select表示子查询。

    <resultMap id="rolePrivilegeListMap2" extends="BaseResultMap"
               type="tk.mybatis.simple.model.SysRole">
        <collection property="privilegeList"
                    fetchType="lazy"
                    column="{roleId = id}"
                    select="tk.mybatis.simple.mapper.SysPrivilegeMapper.selectByRoleId">
        </collection>
    </resultMap>

3.角色mapper中定义一个查询利用resultMap

    <select id="selectRoleByUserId" resultMap="rolePrivilegeListMap2">
        select r.id, r.role_name, r.enabled, r.create_by, r.create_time from sys_role r
        inner join sys_user_role sur on r.id = sur.role_id
        where sur.user_id = #{userId}
    </select>

4.在用户mapper中定义一个resultMap,利用上面通过用户id查角色的select方法

SysUserMapper.xml

    <resultMap id="userRoleListMapSelect" extends="BaseResultMap" type="tk.mybatis.simple.model.SysUser2">
        <collection property="roleList"
                    column="{userId =id}"
                    fetchType="lazy"
                    select="tk.mybatis.simple.mapper.SysRoleMapper.selectRoleByUserId">
        </collection>
    </resultMap>

SysUserMapper中的接口

    /**
     * 查询指定用户角色信息
     * @param id 用户主键
     * @return 角色信息
     */
    SysUser2 selectAllUserAndRolesSelect(Long id);

查询方法

    <select id="selectAllUserAndRolesSelect" resultMap="userRoleListMapSelect">
        select
          id, user_name, user_password, user_email, user_info, head_img, create_time
        from mybatis.sys_user
        where id = #{id}
    </select>

测试方法:

   public void selectAllUserAndRolesSelect() {
        SqlSession sqlSession = getSqlSession();
        try {
            SysUserMapper mapper = sqlSession.getMapper(SysUserMapper.class);

            SysUser2 sysUser = mapper.selectAllUserAndRolesSelect(1L);
            LOGGER.info("用户名:{}", sysUser.getUserName());
            for (SysRole sysRole : sysUser.getRoleList()) {
                LOGGER.info("角色名:{}",sysRole.getRoleName());
                for (SysPrivilege sysPrivilege : sysRole.getPrivilegeList()) {
                    LOGGER.info("权限名:{}",sysPrivilege.getPrivilegeName());
                }
            }
        } finally {
            sqlSession.close();
        }
    }

测试结果:当执行selectAllUserAndRolesSelect方法后,会直接查询用户信息;因为使用了懒加载,当调用了user.getRoleList()方法时,MyBatis执行了第一层的嵌套查询,查询出了该用户的两个角色。对这两个角色进行遍历获取对应的权限信息,因为已经有两个角色,当遍历角色调用sysRole.getPrivilegeList()后就查询角色的权限信息。

需要注意的是,之所以能根据需要查询数据,除了和fetchType有关,还和全局的aggressiveLazyLoading属性有关,这个属性在介绍association时被配置成了false,所以才会起到按需加载的作用。

==>  Preparing: select id, user_name, user_password, user_email, user_info, head_img, create_time from mybatis.sys_user where id = ? 
==> Parameters: 1(Long)
<==    Columns: id, user_name, user_password, user_email, user_info, head_img, create_time
<==        Row: 1, admin, 123456, admin@mybatis.tk, <<BLOB>>, <<BLOB>>, 2021-01-28 21:57:32.0
<==      Total: 1
用户名:admin
==>  Preparing: select r.id, r.role_name, r.enabled, r.create_by, r.create_time from sys_role r inner join sys_user_role sur on r.id = sur.role_id where sur.user_id = ? 
==> Parameters: 1(Long)
<==    Columns: id, role_name, enabled, create_by, create_time
<==        Row: 1, 管理员, 1, 1, 2021-01-28 21:58:32.0
<==        Row: 2, 普通用户, 1, 1, 2021-01-28 21:58:32.0
<==      Total: 2
角色名:管理员
==>  Preparing: select p.id, p.privilege_url, p.privilege_name from sys_privilege p inner join sys_role_privilege srp on p.id = srp.privilege_id where srp.role_id = ? 
==> Parameters: 1(Long)
<==    Columns: id, privilege_url, privilege_name
<==        Row: 1, /users, 用户管理
<==        Row: 3, /logs, 系统日志
<==        Row: 2, /roles, 角色管理
<==      Total: 3
权限名:用户管理
权限名:系统日志
权限名:角色管理
角色名:普通用户
==>  Preparing: select p.id, p.privilege_url, p.privilege_name from sys_privilege p inner join sys_role_privilege srp on p.id = srp.privilege_id where srp.role_id = ? 
==> Parameters: 2(Long)
<==    Columns: id, privilege_url, privilege_name
<==        Row: 4, /persons, 人员维护
<==        Row: 5, /companies, 单位维护
<==      Total: 2
权限名:人员维护
权限名:单位维护

介绍-鉴别器映射(待完善)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值