引言

MyBatis中采用resultMap进行结果集映射,resultMap元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份resultMap能够代替实现同等功能的数千行代码。ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了,可以说结果集映射是MyBatis的灵魂所在之处。

最简单的映射其实已经见过,这是一种隐式的映射,没有显式指定resultMap,句只是简单地将所有的列映射到 HashMap 的键上,这由 resultMap属性指定。虽然在大部分情况下都够用,但是 HashMap 不是一个很好的模型,程序更可能会使用 JavaBean 或POJO(Plain Old Java Objects,普通老式 Java 对象)作为模型。

  1. <select id="selectUserById" resultType="map"> //返回HashMap
  2. select id , name , pwd from user where id = #{id}
  3. </select>

resultMap之子标签result

java的po类属性命名和数据库字段的命名规范是不一样的,这样就会出现不能一一对应,先看一下如何解决属性名和字段名不一致的问题:

package com.wjh.po;

/**
 * @author wjh
 * @date 2021/7/12 20:39
 * @Package com.wjh.po
 */
public class User2 {
    private Integer id;
    private String name;
    private String password;

    public User2() {
    }

    public User2(Integer id, String name, String password) {
        this.id = id;
        this.name = name;
        this.password = password;
    }

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User2{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

实体类的属性是id name password,而数据库字段却是id name pwd,解决方案是使用resultMap映射:

<?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 namespace="com.wjh.dao.User2Mapper">

    <resultMap id="user2Map" type="com.wjh.po.User2">
        <result column="pwd" property="password"/>
    </resultMap>

    <select id="getUserById" resultMap="user2Map">
        select *
        from user
        where id = #{id}
    </select>

</mapper>

其中select标签中的resultMap属性和resultMap中的id必须相对应,resultMap标签的type属性是实体对象的类,其子标签result表示数据库字段pwd映射成实体类属性的password的映射关系,可以有效解决数据库字段和实体类属性的命名冲突。

resultMap之子标签association

association主要用于多对一的处理
背景:多个学生对应一个老师,现在要查询所有学生的相关信息,Student类的属性有id、name、teacher,其中teacher属性是Teacher类的实例对象,Teacher类有id、name;而Student类对应的数据库字段是id、name、tid,Teacher对应的是id、name。

方案一(按查询嵌套处理):

<?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 namespace="com.wjh.dao.StudentMapper">
    <select id="getStudents" resultMap="studentTeacher">
        select *
        from student
    </select>

    <resultMap id="studentTeacher" type="Student">
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>

    <select id="getTeacher" resultType="Teacher">
        select *
        from teacher
        where id = #{id}
    </select>

</mapper>

当需要映射的属性是一个实例对象的时候,resultMap的子标签result已经力不从心了,这时候要使用新的子标签association来映射实例对象。property和column分别是属性和数据库字段,javaType是属性所属的类,select是嵌套查询对应的sql,而column的值则是sql语句中#{}的参数值。如果嵌套查询的sql需要多个参数,那么可以写成“column=”{key=value,key=value}””的形式。如:

方案二(按结果嵌套处理):

<?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 namespace="com.wjh.dao.StudentMapper">
    <select id="getStudents" resultMap="studentTeacher">
        select s.name sname, s.id sid, t.id tid, t.name tname
        from student s,
             teacher t
        where s.tid = t.id
    </select>

    <resultMap id="studentTeacher" type="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" javaType="Teacher">
            <result property="id" column="tid"/>
            <result property="name" column="tname"/>
        </association>
    </resultMap>

</mapper>

可以看到先使用多表查询查询出所有结果,然后再一一映射,先把数据库字段映射成普通属性,在把其余相应的字段映射成teacher属性。

两种处理方式的运行结果:
image.png

resultMap之子标签collection

collection主要用于处理一对多的问题
背景:一个老师对应多个学生,现在要根据老师id来查询一个Teacher对象,Teacher类的属性包括id、name、students,其中students是List集合,Student类的属性有id、name、tid,数据库字段和本章第三点表述的一样。

方案一(按查询嵌套处理):

<?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 namespace="com.wjh.dao.TeacherMapper">
    <select id="getTeacherById" resultMap="teacherMap">
        select *
        from teacher
        where id = #{tid}
    </select>

    <resultMap id="teacherMap" type="Teacher">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <collection property="students" javaType="List" ofType="Student" select="getStudents" column="id"/>
    </resultMap>

    <select id="getStudents" resultType="Student">
        select * from student where tid=#{tid}
    </select>
</mapper>

collection标签的javaType指的是集合的类型(如List、Set、Map),而ofType则指定的集合的具体泛型,column指需要传递给嵌套的sql语句的参数。

方案二(按结果嵌套处理):

<?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 namespace="com.wjh.dao.TeacherMapper">
    <select id="getTeacherById" resultMap="teacherMap">
        select t.id tid, t.name tname, s.id sid, s.name sname, s.id sid
        from teacher t,
             student s
        where t.id = s.tid
          and t.id = #{tid}
    </select>

    <resultMap id="teacherMap" type="Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
        <collection property="students" javaType="List" ofType="Student">
            <result property="id" column="sid"/>
            <result property="name" column="tname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>
</mapper>

collection标签的javaType指的是集合的类型(如List、Set、Map),而ofType则指定的集合的具体泛型。

小结

resultMap常用的映射标签有:id、result、association、collection,id一般用于主键映射,但是result同样可以用于主键映射,因此推荐统一使用result,而且result还可以映射po类对象的普通属性,而id做不到;assocation是用来映射对象属性的,一般用于一对一和多对一的情况;collection是用来映射集合属性的,一般用于一对多,且该标签的JavaType属性是用来指定po中属性的类型,而odType则用来指定集合的具体泛型。