28

MyBatis从入门到精通(九):MyBatis高级结果映射之一对一映射

 4 years ago
source link: https://www.tuicool.com/articles/YfIzEn3
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

最近在读刘增辉老师所著的《MyBatis从入门到精通》一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸!

本篇博客主要讲解MyBatis中实现查询结果一对一映射的3种方式:

  1. 使用别名实现自动映射
  2. 使用resultMap配置
  3. 使用resultMap的association标签配置

1. 使用别名实现自动映射

假设有这样1个需求:根据用户id查询用户信息的同时获取用户拥有的角色,为了举例,我们假设一个用户只能拥有一个角色(实际情况肯定不是这样的)。

一般情况下,不建议直接修改数据库表对应的实体类,所以这里我们新建类SysUserExtend,让它继承自SysUser类,并添加SysRole类型的字段,如下所示:

package com.zwwhnly.mybatisaction.model;

import java.util.List;

public class SysUserExtend extends SysUser {
    /**
     * 用户角色
     */
    private SysRole sysRole;

    public SysRole getSysRole() {
        return sysRole;
    }

    public void setSysRole(SysRole sysRole) {
        this.sysRole = sysRole;
    }
}

然后,我们在接口SysUserMapper中添加如下方法:

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

接着,在对应的SysUserMapper.xml中添加如下代码:

<select id="selectUserAndRoleById" resultType="com.zwwhnly.mybatisaction.model.SysUserExtend">
    SELECT u.id,
           u.user_name userName,
           u.user_password userPassword,
           u.user_email userEmail,
           u.create_time createTime,
           r.id "sysRole.id",
           r.role_name   "sysRole.roleName",
           r.enabled "sysRole.enabled",
           r.create_by   "sysRole.createBy",
           r.create_time "sysRole.createTime"
    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>

这里要注意两点,第一点是这里的resultType,要设置成刚刚新建的SysUserExtend类,第二点是列的别名要设置成 "sysRole.id" 这样的格式,其中sysRole要和SysUserExtend类中的字段名保持一致,id要和SysRole类中的字段名保持一致。

最后,在SysUserMapperTest测试类中添加如下测试方法:

@Test
public void testSelectUserAndRoleById() {
    SqlSession sqlSession = getSqlSession();

    try {
        SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class);

        // 注意这里使用1001这个用户,因为这个用户只有1个角色
        SysUserExtend sysUserExtend = sysUserMapper.selectUserAndRoleById(1001L);

        Assert.assertNotNull(sysUserExtend);
        Assert.assertNotNull(sysUserExtend.getSysRole());
    } finally {
        sqlSession.close();
    }
}

运行测试代码,测试通过,输出日志如下:

DEBUG [main] - ==> Preparing: SELECT u.id, u.user_name userName, u.user_password userPassword, u.user_email userEmail, u.create_time createTime, r.id “sysRole.id”, r.role_name “sysRole.roleName”, r.enabled “sysRole.enabled”, r.create_by “sysRole.createBy”, r.create_time “sysRole.createTime” 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 = ?
DEBUG [main] - ==> Parameters: 1001(Long)
TRACE [main] - <== Columns: id, userName, userPassword, userEmail, createTime, sysRole.id, sysRole.roleName, sysRole.enabled, sysRole.createBy, sysRole.createTime
TRACE [main] - <== Row: 1001, test, 123456, [email protected], 2019-06-27 18:21:07.0, 2, 普通用户, 1, 1, 2019-06-27 18:21:12.0
DEBUG [main] - <== Total: 1

2. 使用resultMap配置

延用上面的需求,不过换成resultMap来配置映射。

首先,我们在接口SysUserMapper中添加如下方法:

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

然后在对应的SysUserMapper.xml中先添加如下resultMap:

<resultMap id="userRoleMap" type="com.zwwhnly.mybatisaction.model.SysUserExtend">
        <id property="id" column="id"/>
        <result property="userName" column="user_name"/>
        <result property="userPassword" column="user_password"/>
        <result property="userEmail" column="user_email"/>
        <result property="userInfo" column="user_info"/>
        <result property="headImg" column="head_img" jdbcType="BLOB"/>
        <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
        <!--role相关属性-->
        <result property="sysRole.id" column="role_id"/>
        <result property="sysRole.roleName" column="role_role_name"/>
        <result property="sysRole.enabled" column="role_enabled"/>
        <result property="sysRole.createBy" column="role_create_by"/>
        <result property="sysRole.createTime" column="role_create_time" jdbcType="TIMESTAMP"/>
</resultMap>

为了避免多张表有相同的列名,所以这里配置role相关属性时,统一加上了”role_“这样的前缀,这里要和下方的查询语句中设置的列的别名保持一致:

<select id="selectUserAndRoleByIdResultMap" resultMap="userRoleMap">
    SELECT  u.id,
            u.user_name,
            u.user_password,
            u.user_email,
            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
    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>

注意事项:这里的查询语句,我们使用的是resultMap而不是resultType。

由于测试代码和输出日志都和1中的几乎一致,所以这里不再列出。

可能有的同学还记得我们之前在SysUserMapper.xml中配置过这样的一个resultMap:

<resultMap id="sysUserMap" type="com.zwwhnly.mybatisaction.model.SysUser">
    <id property="id" column="id"/>
    <result property="userName" column="user_name"/>
    <result property="userPassword" column="user_password"/>
    <result property="userEmail" column="user_email"/>
    <result property="userInfo" column="user_info"/>
    <result property="headImg" column="head_img" jdbcType="BLOB"/>
    <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
</resultMap>

然后就会想,我们刚刚定义的userRoleMap,前半部分和这个配置都是一样的,能复用这个sysUserMap的配置吗?

答案当然是可以,MyBatis是支持resultMap映射继承的。

所以,userRoleMap可以继承自sysUserMap,省略掉重复的配置,优化后的配置如下所示:

<resultMap id="userRoleMap" type="com.zwwhnly.mybatisaction.model.SysUserExtend" extends="sysUserMap">
    <!--role相关属性-->
    <result property="sysRole.id" column="role_id"/>
    <result property="sysRole.roleName" column="role_role_name"/>
    <result property="sysRole.enabled" column="role_enabled"/>
    <result property="sysRole.createBy" column="role_create_by"/>
    <result property="sysRole.createTime" column="role_create_time" jdbcType="TIMESTAMP"/>
</resultMap>

3. 使用resultMap的association标签配置

上面的配置也可以用association标签来配置,配置方式如下所示(实现的效果是一样的):

<resultMap id="userRoleMap" type="com.zwwhnly.mybatisaction.model.SysUserExtend" extends="sysUserMap">
    <association property="sysRole" columnPrefix="role_" javaType="com.zwwhnly.mybatisaction.model.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>

有想法的同学可能会觉得,角色表的映射配置完全可以独立出来配置啊,比如这样:

<resultMap id="roleMap" type="com.zwwhnly.mybatisaction.model.SysRole">
    <id 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"/>
</resultMap>

然后强大的MyBatis可以让association标签来直接使用roleMap,所以上面的association标签配置可以优化为:

<resultMap id="userRoleMap" type="com.zwwhnly.mybatisaction.model.SysUserExtend" extends="sysUserMap">
    <association property="sysRole" columnPrefix="role_"
                     resultMap="roleMap"/>
</resultMap>

此时,roleMap我们是建在SysUserMapper.xml中的,不过实际使用中,这个roleMap放到SysRoleMapper.xml中会更合理一些。

不过要注意的是,将roleMap移动到SysRoleMapper.xml中后,引用roleMap时就要使用全名,如下所示:

<association property="sysRole" columnPrefix="role_"
             resultMap="com.zwwhnly.mybatisaction.mapper.SysRoleMapper.roleMap"/>

否则会报如下错误。

UF7jIj3.png!web

4. 源码及参考

源码地址: https://github.com/zwwhnly/mybatis-action.git ,欢迎下载。

刘增辉《MyBatis从入门到精通》


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK