ResultMap自定义结果集

可以把查询返回的结果集封装成复杂的JavaBean对象

原来的ResultType属性,只能把查询到的结果集转换为简单的JavaBean

 

什么是简单的JavaBean对象?

- 不具有JavaBean和集合类型属性的对象

- 也就是不能建立ORM的多表关联映射

 

问题的引入:

如果字段标识符和数据表不一致,

例如实体类的user的密码字段,现在更改为pwd

【Mybatis】08 ResultMap、Association、分步查询、懒加载_结果集

 

数据表的字段依然为user_password

我们查询这个结果看看

所有的密码字段接受失败

【Mybatis】08 ResultMap、Association、分步查询、懒加载_sql_02

 

那么,该如何解决这个问题?

方案一,在SQL语句中给这个字段起别名

【Mybatis】08 ResultMap、Association、分步查询、懒加载_结果集_03

再次测试,这个数据又能获取到了

【Mybatis】08 ResultMap、Association、分步查询、懒加载_sql_04

 

但是这样的解决方案并不优雅

Mybatis提供了一个解决方案,使用ResultMap对字段标识进行映射绑定

【Mybatis】08 ResultMap、Association、分步查询、懒加载_结果集_05

测试结果,可行

【Mybatis】08 ResultMap、Association、分步查询、懒加载_结果集_06

 

一对一关系案例:

创建数据表

锁表 & 钥匙【一把锁就配一把钥匙】

CREATE TABLE t_lock(
    `id` INT PRIMARY KEY AUTO_INCREMENT,
    `name` VARCHAR(50)

);

CREATE TABLE t_key(
    `id` INT PRIMARY KEY AUTO_INCREMENT,
    `name` VARCHAR(50),
    `lock_id` INT,
    FOREIGN KEY(`lock_id`) REFERENCES t_lock(`id`)
);

添加数据

INSERT INTO t_lock(`name`) 
VALUES
    ('阿里巴巴'),
    ('联想'),
    ('华为');
    
INSERT INTO t_key(`name`,`lock_id`) 
VALUES
    ('马云',1),
    ('任正非',2),
    ('柳传志',3);

【写反了,唉就这样把】

 

创建ORM实体类

锁类

package cn.dai.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author ArkD42
 * @file Mybatis
 * @create 2020 - 05 - 29 - 13:30
 */

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Lock {

    private Integer id;
    private String name;
}

钥匙类

package cn.dai.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author ArkD42
 * @file Mybatis
 * @create 2020 - 05 - 29 - 13:32
 */

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Key {

    private Integer id;
    private String name;

    // 注意这个设置的外键是来自这个锁对象的属性
    private Lock lock;

}

映射器【KeyMaper.xml】

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!-- 映射的Mapper接口名称-->
<mapper namespace="cn.dai.mapper.KeyMapper">

    <select id="queryKeyById" resultType="cn.dai.pojo.Key" parameterType="java.lang.Integer">
        SELECT * FROM t_key WHERE id = #{id}
    </select>

</mapper>

注意!在这里采用的结果集类型,先看看结果如何

测试类

    @Test
    public void sqlTest6(){
        logger.info("- - - - TESTING - - - -");
        SqlSession sqlSession = MybatisUtil.getSqlSession(true);
        KeyMapper keyMapper = sqlSession.getMapper(KeyMapper.class);

        Key key = keyMapper.queryKeyById(2);
        System.out.println(key);

        sqlSession.close();
    }

结果:我们对应的锁类无法获取

【Mybatis】08 ResultMap、Association、分步查询、懒加载_java_07

 

我们需要一个关联查询来实现

首先从SQL语句实现

【查询钥匙表关联锁表,一起获取】

        SELECT
            t_key.*,t_lock.name lock_name
        FROM
            t_key LEFT JOIN t_lock
        ON
            t_key.lock_id = t_lock.id
        WHERE
            t_key.id = #{id}

【就算是这样,没有类型匹配,返回的结果还是和上面一样,这里不展示了】

【留意SELECT子句筛选的字段】

所以需要通过结果集映射标签实现一些类型的绑定

 

使用默认的result标签处理

然后使用我们的结果集映射标签实现关联处理

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!-- 映射的Mapper接口名称-->
<mapper namespace="cn.dai.mapper.KeyMapper">

    <!--
        id 设置这个结果集映射标签的标识,
        type 设置需要转换出来的实体类对象的全限定类名
    -->
    <resultMap id="key" type="key">

        <!-- id标签负责 转换主键列 -> 实体类的属性 -->
        <id column="id" property="id"/>
        <!-- 非主键列 转换给 实体类属性 -->
        <result column="name" property="name"/>
        <!-- 一般匹配的字段不需要显示的写出来,因为是默认的了 -->


        <!--
            property 对应的是实体类的属性
            column 对应的是数据表的列
        -->
        <result column="lock_id" property="lock.id" />
        <result column="lock_name" property="lock.name" />

    </resultMap>

    <!-- 这里的结果集映射就对应上面设置的id -->
    <select id="queryKeyById" resultMap="key" parameterType="int">
        SELECT
            t_key.*,t_lock.name lock_name
        FROM
            t_key LEFT JOIN t_lock
        ON
            t_key.lock_id = t_lock.id
        WHERE
            t_key.id = #{id}
    </select>

</mapper>

测试结果

【Mybatis】08 ResultMap、Association、分步查询、懒加载_实体类_08

千万要注意,要实现这个锁的名称的输出,就要留意上面的SELECT 筛选的字段

 

可以通过别名映射实现一些实体类的绑定

在这里是因为左外链接,所以可以看到,钥匙表是没有写这个表名的

【Mybatis】08 ResultMap、Association、分步查询、懒加载_字段_09

 

 或者使用Association标签实现映射

 在结果集中设置关联映射标签,绑定属性标识和所对应的类型

其次声明关联表的属性映射

       <association property="lock" javaType="cn.dai.pojo.Lock">
           <id column="lock_id" property="id"/>
           <result column="lock_name" property="name" />
       </association>

【Mybatis】08 ResultMap、Association、分步查询、懒加载_字段_10

 

Association定义分布查询

什么意思?

可以通过一个查询得到子对象

分两种加载,立即加载和懒加载

【Mybatis】08 ResultMap、Association、分步查询、懒加载_字段_11

一张表可能存在50多列,其中主要常用的是最前面的6列

后面的44列不一定马上需要,所以我们的查询应该拆分成两次查询

 

我们声明一个两步查询的方法

【Mybatis】08 ResultMap、Association、分步查询、懒加载_sql_12

Key queryKeyByIdForTwoStep(Integer id);

对副表的二次查询在锁表中编写

package cn.dai.mapper;

import cn.dai.pojo.Lock;

/**
 * @author ArkD42
 * @file Mybatis
 * @create 2020 - 05 - 30 - 11:59
 */
public interface LockMapper {
    
    Lock queryLockById(Integer id);
    
}

锁表的映射器

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!-- 映射的Mapper接口名称-->
<mapper namespace="cn.dai.mapper.LockMapper">
    
    <select id="queryLockById" resultType="cn.dai.pojo.Lock" >
        select * from t_lock where id = #{id}
    </select>

</mapper>

钥匙表的映射器

    <resultMap id="key2" type="cn.dai.pojo.Key">

        <id column="id" property="id"/>
        <result column="name" property="name"/>
        
        <!-- 
            select 调用另外一个映射器的查询方法,来获取对象的结果
            column 把这钥匙表的查询SQL的字段结果返回给这个关联查询调用【就是那个查询的参数】
            
        -->
        <association 
                property="lock" 
                javaType="cn.dai.pojo.Lock" 
                select="cn.dai.mapper.LockMapper.queryLockById"
                column="lock_id"
        />

    </resultMap>

    <select id="queryKeyByIdForTwoStep" resultMap="key2" parameterType="int">
        SELECT
            id,name,lock_id
        FROM
            t_key
        WHERE
            t_key.id = #{id}
    </select>

测试结果

【Mybatis】08 ResultMap、Association、分步查询、懒加载_结果集_13

可以看到这样查询分了两次执行,并且把字段的参数传递给下一个查询的使用

 

开启懒加载查询

即延迟加载,减少非必要性的查询

优化数据库性能,需要在全局配置中开启

        <!-- 开启懒加载 -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>

【Mybatis】08 ResultMap、Association、分步查询、懒加载_sql_14

    @Test
    public void sqlTest7(){
        logger.info("- - - - TESTING - - - -");
        SqlSession sqlSession = MybatisUtil.getSqlSession(true);
        KeyMapper keyMapper = sqlSession.getMapper(KeyMapper.class);

        Key key = keyMapper.queryKeyByIdForTwoStep(2);

        //System.out.println(key);

        //开启懒加载是什么效果?如果我们不调用附表的信息,第二个查询是不会调用的
        System.out.println(key.getName());

        sqlSession.close();
    }

结果

【Mybatis】08 ResultMap、Association、分步查询、懒加载_结果集_15

 

实际作用远不止这些,详见官方文档:

https://mybatis.org/mybatis-3/zh/sqlmap-xml.html