MyBatis从入门到精通(第6章):MyBatis 高级查询->6.1.1高级结果映射之一对一映射

jdk1.8、MyBatis3.4.6、MySQL数据库5.6.45、IntelliJ IDEA 2019.2.4

本章主要包含的内容为 MyBatis 的高级结果映射,主要处理数据库一对一、一对多的查询,另外就是在 MyBatis 中使用存储过程的方法,处理存储过程的入参和出参方法,最后会介绍 Java 中的枚举方法和数据库表字段的处理方法

6.1 高级结果映射 


在关系型数据库中,我们经常要处理一对一、一对多的关系

在 RBAC 权限系统中还存在着一个用户拥有多个角色、一个角色拥有多个权限这样复杂的嵌套关系。使用已经学会的 MyBatis 技巧都可以轻松地解决这种复杂的关系。在面对这种关系的时候,我们可能要写多个方法分别查询这些数据,然后再组合到一起。这种处理方式特别适合用在大型系统上,由于分库分表,这种用法可以减少表之间的关联查询,方便系统进行扩展 

但是在一般的企业级应用中,使用 MyBatis 的高级结果映射便可以轻松地处理这种一对一、一对多的关系。本节将带领大家学习与高级结果映射相关的内容

 6.1.1 一对一映射

假设在 RBAC 权限系统中,一个用户只能拥有一个角色,为了举例,先把用户和角色之间的关系限制为一对一的关系。在 2.3 节中介绍了一个 selectRolesByUserId 方法,这个方法实际上就已经是一个一对一关系了一对一映射因为不需要考虑是否存在重复数据,因此使用起来很简单而且可以直接使用 MyBatis 的自动映射。此处参考 2.3 节中的方法,使用自动映射实现在查询用户信息的同时获取用户拥有的角色

 6.1.1.1 使用自动映射处理一对一关系

一个用户拥有一个角色,因此先在 SysUser 类中增加 SysRole 字段,代码如下

/**
 * 用户表
 */
public class SysUser implements Serializable {
    private static final long serialVersionUID = -328602757171077630L;
    /**
     * 用户ID
     */
    private Long id;
    /**
     * 用户名
     */
    private String userName;
    /**
     * 密码
     */
    private String userPassword;
    /**
     * 邮箱
     */
    private String userEmail;
    /**
     * 简介
     */
    private String userInfo;
    /**
     * 头像
     */
    private byte[] headImg;
    /**
     * 创建时间
     */
    private Date createTime;
    
    /**
     * 用户角色
     */
    private SysRole role;
    
    /**
     * 用户的角色集合
     */
    private List<SysRole> roleList;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserPassword() {
        return userPassword;
    }

    public void setUserPassword(String userPassword) {
        this.userPassword = userPassword;
    }

    public String getUserEmail() {
        return userEmail;
    }

    public void setUserEmail(String userEmail) {
        this.userEmail = userEmail;
    }

    public String getUserInfo() {
        return userInfo;
    }

    public void setUserInfo(String userInfo) {
        this.userInfo = userInfo;
    }

    public byte[] getHeadImg() {
        return headImg;
    }

    public void setHeadImg(byte[] headImg) {
        this.headImg = headImg;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public SysRole getRole() {
        return role;
    }

    public void setRole(SysRole role) {
        this.role = role;
    }

    public List<SysRole> getRoleList() {
        return roleList;
    }

    public void setRoleList(List<SysRole> roleList) {
        this.roleList = roleList;
    }

}
View Code

使用自动映射就是通过别名让 MyBatis 自动将值匹配到对应的字段上,简单的别名映射如 user_name 对应 userName除此之外 MyBatis 还支持复杂的属性映射,可以多层嵌套,例如将 role.role _name 映射到 role.roleName 上 

下面根据自动映射的规则,在 UserMapper.xml 中增加如下方法

    <!--第6章 6.1 高级结果映射-->
    <select id="selectUserAndRoleById" resultType="cn.bjut.simple.model.SysUser">
        select
               u.id,
               u.user_name userName,
            u.user_password userPassword,
            u.user_email userEmail,
            u.user_info userInfo,
            u.head_img headImg,
            u.create_time createTime,
            r.id "role.id",
            r.role_name "role.roleName",
            r.enabled "role.enabled",
            r.create_by "role.createBy",
            r.create_time "role.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>
View Code

注意上述方法中 sys_role 查询列的别名都是“role.”前缀,通过这种方式将 role 的属性都映射到了 SysUser 的 role 成员变量属性上

UserMapper 的接口中添加对应的方法,代码如下

    /**
     * 第6章 6.1 高级结果映射
     * 根据用户 id 获取用户信息和用户的角色信息
     *
     * @param id
     * @return
     */
    SysUser selectUserAndRoleById(Long id);

针对该方法编写测试代码如下

    //第6章 
    @Test
    public void testSelectUserAndRoleById(){
        //获取 sqlSession
        SqlSession sqlSession = getSqlSession();
        try {
            //获取 UserMapper 接口
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            //特别注意,在我们测试数据中,id = 1L 的用户有两个角色
            //由于后面覆盖前面的,因此只能得到最后一个角色
            //我们这里使用只有一个角色的用户(id = 1001L)
            //可以用 selectUserAndRoleById2 替换进行测试
            SysUser user = userMapper.selectUserAndRoleById(1001L);
            //user 不为空
            Assert.assertNotNull(user);
            //user.role 也不为空
            Assert.assertNotNull(user.getRole());
        } finally {
            //不要忘记关闭 sqlSession
            sqlSession.close();
        }
    }
public void testSelectUserAndRoleById()

执行该测试,在获取 user 后面的代码处设置断点,此时查询结果如图 6-1 所示

 

DEBUG [main] - ==>  Preparing: select u.id, u.user_name, u.user_password, 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 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, 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
TRACE [main] - <==        Row: 1001, test, 123456, test@mybatis.tk, <<BLOB>>, <<BLOB>>, 2019-07-12 17:01:52.0, 2, 普通用户, 1, 1, 2019-11-17 19:06:00.0
DEBUG [main] - <==      Total: 1

 

通过 SQL 日志可以看到已经查询出的一条数据,MyBatis 将这条数据映射到了两个类中,像这种通过一次查询将结果映射到不同对象的方式,称之为关联的嵌套结果映射

关联的嵌套结果映射需要关联多个表将所有需要的值一次性查询出来。这种方式的好处是减少数据库查询次数,减轻数据库的压力,缺点是要写很复杂的 SQL,并且当嵌套结果更复杂时,不容易一次写正确,由于要在应用服务器上将结果映射到不同的类上,因此也会增加应用服务器的压力当一定会使用到嵌套结果,并且整个复杂的 SQL 执行速度很快时,建议使用关联的嵌套结果映射

 

 6.1.1.2 使用 resultMap 配置一对一映射

除了使用 MyBatis 的自动映射来处理一对一嵌套外,还可以在 XML 映射文件中配置结果映射。上一节中的复杂对象映射也可以使用相同效果的 resultMap 进行配置,使用 resultMap 实现和上一节中的例子相同的效果

UserMapper.xml 中增加如下的 resultMap 配置

这种配置和上一节相似的地方在于,role 中的 property 配置部分使用“role.”前缀。在 column 部分,为了避免不同表中存在相同的列,所有可能重名的列都增加了“role_”前缀。使用这种方式配置的时候,还需要在查询时设置不同的别名。针对该方法在 UserMapper.xml 中增加一个 selectUserAndRoleById 2 方法,代码如下

    <select id="selectUserAndRoleById2" resultMap="userRoleMap2">
        select
            u.id,
            u.user_name,
            u.user_password,
            u.user_email,
            u.user_info,
            u.head_img,
            u.create_time,
            r.id role_id,
            r.role_name,
            r.enabled enabled,
            r.create_by 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 来设置,而是需要使用 resultMap 属性将其配置为上面的 userRoleMap2 。注意 SQL 中只有 sys_role 部分列为了防止重名而增加了列命名,并且别名和 resultMap 中配置的 column 一致。在 UserMapper 接口中增加对应的方法,代码如下

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

该接口的测试方法和 selectUserAndRoleById 一模一样,只需要把调用 selectUserAndRoleById 的方法改为 selectUserAndRoleById 2 即可,测试代码以及日志输出都和 selectUserAndRoleById 方法一样,这里不在重复

用过上一种写法后再看这一种写法就会发现 resultMap 非常烦琐,不仅没有方便使用反而增加了更多的工作量。MyBatis 是支持 resultMap 映射继承的,因此要先简化上面的 resultMap 配置。在这个映射文件中本就存在一个 userMap 的映射配置(虽然这个 userMap 是第 2 章中手写的,但是学过第 5 章后,使用 MyBatis 代码生成器生成的代码都包含基础的 resultMap 配置,这个配置不需要手写,所以很简单,因此 userRoleMap 只需要继承 userMap,然后添加 role 特有的配置即可 userRoleMap 修改后的代码如下

 

    <resultMap id="userMap" type="cn.bjut.simple.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>

    <resultMap id="userRoleMap2" extends="userMap" type="cn.bjut.simple.model.SysUser">
        <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>
View Code "userRoleMap2" extends="userMap"

 6.1.1.3 使用 resultMap 的 association 标签配置一对一映射

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

在上面配置的基础上,再做修改,改成 association 标签的配置方式,代码如下

  association 标签包含以下属性

· property:对应实体类中的属性名,必填项

· javaType:属性对应的 Java 类型

· resultMap :可以直接使用现有的 resultMap ,而不需要在这里配置

· columnPrefix :查询列的前缀,配置前缀后,在子标签配置 result 的 column 时可以省略前缀

因为上面配置了属性 role,因此在 association 内部配置 result 的 property 属性时,直接按照 SysRole 对象中的属性名配置即可另外我们还配置了 columnPrefix= "role_",在写 SQL 的时候,和 sys_role 表相关的查询列的别名都要有“role_”前缀,在内部 result 配置 column 时,需要配置成去掉前缀的列名,MyBatis 在映射结果时会自动使用前缀和 column 值的组合去 SQL 查询的结果中取值。这种配置方式实际上是很方便的,但是目前此处的写法无法体现,后面改进的例子会让大家看到效果

对于前面提到的修改后的 resultMap ,因为配置了列的前缀,因此还需要修改 SQL,代码如下 

    <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,
            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>

注意和 sys_role 相关列的别名,都已经改成了“role_”前缀,特别注意 role_name 增加前缀后为 role_role_name。修改完成后,可以执行 selectUserAndRoleById 2 方法的测试,该测试的结果仍然会和之前的结果一样

 

    <resultMap id="BaseResultMap" type="cn.bjut.simple.model.SysRole">
        <!--
          WARNING - @mbg.generated
          This element is automatically generated by MyBatis Generator, do not modify.
        -->
        <id column="id" jdbcType="BIGINT" property="id" />
        <result column="role_name" jdbcType="VARCHAR" property="roleName" />
        <result column="enabled" jdbcType="INTEGER" property="enabled" />
        <result column="create_by" jdbcType="BIGINT" property="createBy" />
        <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
    </resultMap>

    <resultMap id="userRoleMap3" extends="userMap" type="cn.bjut.simple.model.SysUser">
        <association property="role" columnPrefix="role_" resultMap="BaseResultMap"/>
    </resultMap>

 

MyBatis 默认会给 roleMap 添加当前命名空间的前缀,代码如下

tk.mybatis.simple.mapper.UserMapper.roleMap

在移动 roleMap 之前,这个完整的地址是正确的,移动后便找不到 resultMap 了,此时必须指定完整的名字才能找到。引用 resultMap 时一定要注意这一点

写到这种程度已经很简单了,和最开始的方式相比少了主表的一部分别名,但从表仍然需要别名,另外还多了 resultMap 配置

目前已经讲到的这 3 种情况都属于“关联的嵌套结果映射”,即通过一次 SQL 查询根据表或指定的属性映射到不同的对象中。除了这种方式,还有一种“关联的嵌套查询”,也就意味着还有额外的查询,下面来看第 4 种情况

 6.1.1.4 association 标签的嵌套查询

除了前面 3 种通过复杂的 SQL 查询获取结果,还可以利用简单的 SQL 通过多次查询转换为我们需要的结果,这种方式与根据业务逻辑手动执行多次 SQL 的方式相像,最后会将结果组合成一个对象

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

· select:另一个映射查询的 id,MyBatis 会额外执行这个查询获取嵌套对象的结果

· column:列名(或别名,将主查询中列的结果作为嵌套查询的参数,配置方式如 column={prop1=col1,prop2=col2},prop1 和 prop2 将作为嵌套查询的参数

· fetchType :数据加载方式,可选值为 lazy 和 eager,分别为延迟加载和积极加载,这个配置会覆盖全局的 lazyLoadingEnabled 配置

使用嵌套查询的方式配置一个和前面功能一样的方法,首先在 UserMapper.xml 中创建如下的 resultMap

    <resultMap id="userRoleMapSelect" extends="userMap" type="cn.bjut.simple.model.SysUser">
        <association property="role"
                     select="cn.bjut.simple.mapper.RoleMapper.selectRoleById"
                     column="{id=role_id}"/>
    </resultMap>

接着去RoleMapper接口添加这个方法。

    //第6章 6.1.1.4 association 标签的嵌套查询
    abstract SysRole selectRoleById(Long id);

然后在RoleMapper.xml中,创建一个id= "selectRoleById" 的查询方法,代码如下

    <resultMap id="BaseRoleMap" type="cn.bjut.simple.model.SysRole">
        <!--
          WARNING - @mbg.generated
          This element is automatically generated by MyBatis Generator, do not modify.
        -->
        <id column="id" jdbcType="BIGINT" property="id" />
        <result column="role_name" jdbcType="VARCHAR" property="roleName" />
        <result column="enabled" jdbcType="INTEGER" property="enabled" />
        <result column="create_by" jdbcType="BIGINT" property="createBy" />
        <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
    </resultMap>

    <select id="selectRoleById" resultMap="BaseRoleMap">
        select * from sys_role where id = #{id}
    </select>
View Code select id="selectRoleById"

然后回到UserMapper.xml创建一个使用 userRoleMapSelect 的查询方法,代码如下
    <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,
            ur.role_id
        from sys_user u
        inner join sys_user_role ur on u.id = ur.user_id
        where u.id = #{id}
    </select>
针对上面这个方法,在 UserMapperTest 中编写测试如下。
    //6.1.1.4
    @Test
    public void testSelectUserAndRoleByIdSelect1() {
        //获取 sqlSession
        SqlSession sqlSession = getSqlSession();
        try {
            //获取 UserMapper 接口
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            //特别注意,在我们测试数据中,id = 1L 的用户有两个角色
            //由于后面覆盖前面的,因此只能得到最后一个角色
            //我们这里使用只有一个角色的用户(id = 1001L)
            SysUser user = userMapper.selectUserAndRoleByIdSelect(1001L);
            //user 不为空
            Assert.assertNotNull(user);
            //user.role 也不为空
            Assert.assertNotNull(user.getRole());
        } finally {
            //不要忘记关闭 sqlSession
            sqlSession.close();
        }
    }
testSelectUserAndRoleByIdSelect1

因为嵌套查询会多执行 SQL 查询,因此这个测试的输出日志是我们最关心的,该测试输出日志内容如下

DEBUG [main] - ==>  Preparing: select u.id, u.user_name, u.user_password, u.user_email, u.user_info, u.head_img, u.create_time, ur.role_id from sys_user u inner join sys_user_role ur on u.id = ur.user_id where u.id = ? 
DEBUG [main] - ==> Parameters: 1001(Long)
TRACE [main] - <==    Columns: id, user_name, user_password, user_email, user_info, head_img, create_time, role_id
TRACE [main] - <==        Row: 1001, test, 123456, test@mybatis.tk, <<BLOB>>, <<BLOB>>, 2019-07-12 17:01:52.0, 2
DEBUG [main] - ====>  Preparing: select * from sys_role where id = ? 
DEBUG [main] - ====> Parameters: 2(Long)
TRACE [main] - <====    Columns: id, role_name, enabled, create_by, create_time
TRACE [main] - <====        Row: 2, 普通用户, 1, 1, 2019-11-17 19:06:00.0
DEBUG [main] - <====      Total: 1
DEBUG [main] - <==      Total: 1

结果和我们想的一致,因为第一个 SQL 的查询结果只有一条,所以根据这一条数据的 role_id 关联了另一个查询,因此执行了两次 SQL

这种配置方式符合开始时预期的结果,但是由于嵌套查询会多执行 SQL,所以还要考虑更多情况。在这个例子中,是否一定会用到 SysRole 呢?如果查询出来并没有使用,那不就白白浪费了一次查询吗?如果查询的不是 1 条数据,而是 N 条数据,那就会出现 N+1 问题,主 SQL 会查询一次,查询出 N 条结果,这 N 条结果要各自执行一次查询,那就需要进行 N 次查询。如何解决这个问题呢

在上面介绍 association 标签的属性时,介绍了 fetchType 数据加载方式,这个方式可以帮我们实现延迟加载,解决 N+1 的问题。按照上面的介绍,需要把 fetchType 设置为 lazy,这样设置后,只有当调用 getRole)方法获取 role 的时候,MyBatis 才会执行嵌套查询去获取数据。首先修改 userRoleMapSelect 方法,增加 fetchType 属性,代码如下

    <resultMap id="userRoleMapSelect2" extends="userMap" type="cn.bjut.simple.model.SysUser">
        <association property="role" fetchType="lazy"
                     select="cn.bjut.simple.mapper.RoleMapper.selectRoleById"
                     column="{id=role_id}"/>
    </resultMap>

修改测试方法如下

    @Test
    public void testSelectUserAndRoleByIdSelect2() {
        //获取 sqlSession
        SqlSession sqlSession = getSqlSession();
        try {
            //获取 UserMapper 接口
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            //特别注意,在我们测试数据中,id = 1L 的用户有两个角色
            //由于后面覆盖前面的,因此只能得到最后一个角色
            //我们这里使用只有一个角色的用户(id = 1001L)
            SysUser user = userMapper.selectUserAndRoleByIdSelect(1001L);
            //user 不为空
            Assert.assertNotNull(user);
            //user.role 也不为空
            //System.out.println("调用 user.equals(null)");
            //user.equals(null);
            System.out.println("---------------------------------");
            System.out.println("调用 user.getRole()");
            Assert.assertNotNull(user.getRole());
        } finally {
            //不要忘记关闭 sqlSession
            sqlSession.close();
        }

    }
testSelectUserAndRoleByIdSelect2

 

    <settings>
        <!--下划线转驼峰-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!--配置日志输出接口-->
        <setting name="logImpl" value="LOG4J"/>
        <!--其他配置-->
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

增加这个配置,再次执行测试,这次输出的日志如下

DEBUG [main] - ==> Preparing: select u.id, u.user_name, u.user_password, u.user_email, u.user_info, u.head_img, u.create_time, ur.role_id from sys_user u inner join sys_user_role ur on u.id = ur.user_id where u.id = ?
DEBUG [main] - ==> Parameters: 1001(Long)
TRACE [main] - <== Columns: id, user_name, user_password, user_email, user_info, head_img, create_time, role_id
TRACE [main] - <== Row: 1001, test, 123456, test@mybatis.tk, <<BLOB>>, <<BLOB>>, 2019-07-12 17:01:52.0, 2
DEBUG [main] - <== Total: 1
---------------------------------
调用 user.getRole()
DEBUG [main] - ==> Preparing: select * from sys_role where id = ?
DEBUG [main] - ==> Parameters: 2(Long)
TRACE [main] - <== Columns: id, role_name, enabled, create_by, create_time
TRACE [main] - <== Row: 2, 普通用户, 1, 1, 2019-11-17 19:06:00.0
DEBUG [main] - <== Total: 1

从日志中可以看出,执行的结果和预期的结果一样,在调用 getRole)方法后才执行嵌套 SQL 查询结果

特别提醒

许多对延迟加载原理不太熟悉的朋友会经常遇到一些莫名其妙的问题:有些时候延迟加载可以得到数据,有些时候延迟加载就会报错,为什么会出现这种情况呢

MyBatis 延迟加载是通过动态代理实现的,当调用配置为延迟加载的属性方法时,动态代理的操作会被触发,这些额外的操作就是通过 MyBatis 的 SqlSession 去执行嵌套 SQL 的。由于在和某些框架集成时 SqlSession 的生命周期交给了框架来管理,因此当对象超出 SqlSession 生命周期调用时,会由于链接关闭等问题而抛出异常。在和 Spring 集成时,要确保只能在 Service 层调用延迟加载的属性。当结果从 Service 层返回至 Controller 层时,如果获取延迟加载的属性值,会因为 SqlSession 已经关闭而抛出异常

 

虽然这个方法已经满足了我们的要求,但是有些时候还是需要在触发某方法时将所有的数据都加载进来,而我们已经将 aggressiveLazyLoading 设置为 false,这种情况又该怎么解决呢

MyBatis 仍然提供了参数 lazyLoadTriggerMethods 帮助解决这个问题,这个参数的含义是当调用配置中的方法时,加载全部的延迟加载数据默认值为"equals,clone,hashCode,toString"因此在使用默认值的情况下,只要调用其中一个方法就可以实现加载调用对象的全部数据。修改测试,修改部分代码如下

    @Test
    public void testSelectUserAndRoleByIdSelect3() {
        //获取 sqlSession
        SqlSession sqlSession = getSqlSession();
        try {
            //获取 UserMapper 接口
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            //特别注意,在我们测试数据中,id = 1L 的用户有两个角色
            //由于后面覆盖前面的,因此只能得到最后一个角色
            //我们这里使用只有一个角色的用户(id = 1001L)
            SysUser user = userMapper.selectUserAndRoleByIdSelect(1001L);
            //user 不为空
            Assert.assertNotNull(user);
            //user.role 也不为空
            System.out.println("调用 user.equals(null)");
            user.equals(null);
            System.out.println("---------------------------------");
            System.out.println("调用 user.getRole()");
            Assert.assertNotNull(user.getRole());
        } finally {
            //不要忘记关闭 sqlSession
            sqlSession.close();
        }

    }
public void testSelectUserAndRoleByIdSelect3()
DEBUG [main] - ==>  Preparing: select u.id, u.user_name, u.user_password, u.user_email, u.user_info, u.head_img, u.create_time, ur.role_id from sys_user u inner join sys_user_role ur on u.id = ur.user_id where u.id = ? 
DEBUG [main] - ==> Parameters: 1001(Long)
TRACE [main] - <==    Columns: id, user_name, user_password, user_email, user_info, head_img, create_time, role_id
TRACE [main] - <==        Row: 1001, test, 123456, test@mybatis.tk, <<BLOB>>, <<BLOB>>, 2019-07-12 17:01:52.0, 2
DEBUG [main] - <==      Total: 1
调用 user.equals(null)
DEBUG [main] - ==>  Preparing: select * from sys_role where id = ? 
DEBUG [main] - ==> Parameters: 2(Long)
TRACE [main] - <==    Columns: id, role_name, enabled, create_by, create_time
TRACE [main] - <==        Row: 2, 普通用户, 1, 1, 2019-11-17 19:06:00.0
DEBUG [main] - <==      Total: 1
---------------------------------
调用 user.getRole()

 从日志中可以看到,调用 equals 方法后就触发了延迟加载属性的查询,这种方式可以满足需要


 

 这一节中,我们通过讲解 4 种方式的一对一查询,循序渐进地为大家介绍了高级映射中的关键内容。除了基本的属性,还讲解了 resultMap 的继承、关联的嵌套查询、关联的嵌套结果查询。在关联的嵌套查询中,我们又介绍了延迟加载的详细用法

 这一节的内容(尤其是 6.1.1.4 节)非常重要,在下一节关于一对多映射的介绍中,嵌套查询和延迟加载与本节介绍的内容完全一样,唯一不同的只是映射结果的数量,继续来看一对多映射

 

================================================================

 

end

posted @ 2019-11-22 14:22  Marlon康  阅读(220)  评论(0编辑  收藏  举报