MyBatis学习笔记6、结果集映射ResultMap

6、结果集映射ResultMap

6.1、如果类变量名和字段名不一样会发生什么

如果我们把 pojo 类里的 pwd 改成 passwd。就会导致属性名和字段名不一致(字段名是pwd)。

private int id;
private String name;
private String passwd;

如果我们执行测试代码,结果是:

User{id=1, name='dzy', pwd='null'}

获取不到密码,原因是 mybatis 把select * from mybatis.user where id = #{id}解析成了

select id, name, pwd from mybatis.user where id = #{id}

pwd 和 passwd 没有关系,所以passwd不会被赋值。

如果我们修改 sql 为:

select id, name, pwd as passwd from mybatis.user where id = #{id}

再次执行,结果正确:

User{id=1, name='dzy', pwd='123456'}

6.2、resultMap

上面的在 sql 语句里起别名是一个解决方法,但是使用resultMap是更好的方法

在 mapper.xml 中使用 resultMap 标签,如:

<!-- namespace要绑定一个mapper接口 -->
<mapper namespace="com.dzy.dao.UserMapper">
    <!-- 结果集映射 typer="User"是因为给实体类起了别名所以不写全路径-->
    <resultMap id="userMap" type="User">
        <!-- 列是数据库的字段,property是类的属性 -->
        <result column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="pwd" property="passwd"/>
    </resultMap>
    <!-- resultMap和上面的resultMap标签里的id一致 -->
    <select id="getUserById" parameterType="int" resultMap="userMap">
        select * from mybatis.user where id = #{id}
    </select>
</mapper>

执行测试,结果正确:

User{id=1, name='dzy', pwd='123456'}
  • resultMap 元素是 MyBatis 中最重要最强大的元素
  • ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
  • 变量名和字段名一样的,不需要配置映射,所以前面的xml可以少些两行。
<mapper namespace="com.dzy.dao.UserMapper">
    <!-- 结果集映射 typer="User"是因为给实体类起了别名所以不写全路径-->
    <resultMap id="userMap" type="User">
        <!-- 列是数据库的字段,property是类的属性 -->
        <result column="pwd" property="passwd"/>
    </resultMap>
    <!-- resultMap和上面的resultMap标签里的id一致 -->
    <select id="getUserById" parameterType="int" resultMap="userMap">
        select * from mybatis.user where id = #{id}
    </select>
</mapper>

只需要映射 pwd 这个不一样的,id 和 name 的变量名和字段名一样,就不用映射了。

6.3、准备多表查询

首先新建一个 teacher 表

use `mybatis`;
create table `teacher`(
    `id` int(11) auto_increment,
    `name` varchar(30) default null,
    primary key (`id`)
) engine = INNODB default charset = utf8

向 teacher 表中插入数据

insert into mybatis.teacher (`id`, `name`) VALUES (1, '老师A')

再新建一个 student 表

create table `student`(
    `id` int(11) auto_increment,
    `name` varchar(30) default null,
    `tid` int(11) default null,
    primary key (`id`),
    key `fktid` (`tid`),
    constraint `fktid` foreign key (`tid`) references `teacher` (`id`)
)

向 student 表中插入数据

insert into mybatis.student (id, name, tid) VALUES
(1, '小明', 1),
(2, '小红', 1),
(3, '小张', 1),
(4, '小李', 1),
(5, '小王', 1)

创建实体类 teacher ,使用 lombok 的注解

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    private int id;
    private String name;
}

创建实体类 student

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private int id;
    private String name;
    // 直接用引用关联,int在这里不合适
    // 因为int总得有个值,那不关联应该写几呢
    private Teacher teacher;
}

6.4、多对一的处理

刚刚建好的数据表中,多个学生对应一个老师,那么获取所有学生及其对应老师就是多对一的查询

6.4.1、按照查询嵌套处理

设计接口:

public interface StudentMapper {
    // 查询所有学生和对应的老师
    public List<Student> getStudents();
}

配置 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">
<!-- namespace要绑定一个mapper接口 -->
<mapper namespace="com.dzy.dao.StudentMapper">
    <resultMap id="studentMap" type="Student">
        <!-- id标签是特殊的result,只能用于主键映射,result通用 -->
        <id property="id" column="id" />
        <result property="name" column="name" />
        <!-- result只能映射简单的类型,复杂的对象要单独处理 -->
        <!-- 对象使用association,集合使用collection -->
        <!-- javaType是子查询的返回类型,select是子查询的id -->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>
    
    <select id="getStudents" resultMap="studentMap">
        select * from mybatis.student
    </select>

    <select id="getTeacher" resultType="Teacher">
        <!-- 这里#{}里可以随便写,最好和association标签里的colum一致 -->
        select * from mybatis.teacher where id = #{tid}
    </select>

</mapper>

测试:

public class StudentDaoTest {
    @Test
    public void TestGetStudents() {
        try(SqlSession session = MybatisUtils.getSqlSession()) {
            StudentMapper mapper = session.getMapper(StudentMapper.class);
            List<Student> studentList = mapper.getStudents();
            for (Student s : studentList){
                System.out.println(s);
            }
        }
    }
}

结果:

Opening JDBC Connection
Created connection 1423768154.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@54dcfa5a]
==>  Preparing: select * from mybatis.student 
==> Parameters: 
<==    Columns: id, name, tid
<==        Row: 1, 小明, 1
====>  Preparing: select * from mybatis.teacher where id = ? 
====> Parameters: 1(Integer)
<====    Columns: id, name
<====        Row: 1, 老师A
<====      Total: 1
<==        Row: 2, 小红, 1
<==        Row: 3, 小张, 1
<==        Row: 4, 小李, 1
<==        Row: 5, 小王, 1
<==      Total: 5
Student(id=1, name=小明, teacher=Teacher(id=1, name=老师A))
Student(id=2, name=小红, teacher=Teacher(id=1, name=老师A))
Student(id=3, name=小张, teacher=Teacher(id=1, name=老师A))
Student(id=4, name=小李, teacher=Teacher(id=1, name=老师A))
Student(id=5, name=小王, teacher=Teacher(id=1, name=老师A))
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@54dcfa5a]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@54dcfa5a]
Returned connection 1423768154 to pool.

6.4.2、按照结果嵌套处理

<mapper namespace="com.dzy.dao.StudentMapper">
    <resultMap id="studentMap" type="Student">
        <!-- id标签是特殊的result,只能用于主键映射,result通用 -->
        <id property="id" column="sid" />
        <result property="name" column="sn" />
        <!-- result只能映射简单的类型,复杂的对象要单独处理 -->
        <!-- 对象使用association,集合使用collection -->
        <!-- javaType是子查询的返回类型,select是子查询的id -->
        <association property="teacher" javaType="Teacher">
            <id property="id" column="tid"/>
            <result property="name" column="tn" />
        </association>
    </resultMap>
    
    <select id="getStudents" resultMap="studentMap">
        select s.id sid, s.name sn, t.id tid, t.name tn from student s, teacher t where s.tid = t.id
    </select>

</mapper>

这种方法只需要一个 sql,但是association标签里要写的东西多一些。

之前的方法对应子查询,这种方法对应联表查询。

6.5、一对多处理

6.5.1、按结果(联表)

首先修改实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private int id;
    private String name;
    // 直接用引用关联,int在这里不合适
    // 因为int总得有个值,那不关联应该写几呢
    private Teacher teacher;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    private int id;
    private String name;
    // 一个老师关联多个学生
    private List<Student> studentList;
}

然后是 DAO 接口 TeacherMapper.java

public interface TeacherMapper {
    Teacher getTeacherById(@Param("tid") int id);
}

然后是 TeacherMapper.xml

<mapper namespace="com.dzy.dao.TeacherMapper">

    <resultMap id="teacherMap" type="Teacher">
        <id property="id" column="tid" />
        <result property="name" column="tn"/>
        <!-- 集合中的泛型使用ofType -->
        <collection property="studentList" ofType="Student">
            <result property="id" column="sid"/>
            <result property="name" column="sn"/>
        </collection>
    </resultMap>

    <select id="getTeacherById" resultMap="teacherMap">
        select t.id tid, t.name tn, s.id sid, s.name sn from teacher t, student s where t.id = s.tid and t.id = #{tid}
    </select>
</mapper>

最后是测试类

public class TeacherDaoTest {
    @Test
    public void testSelectTeacherByID() {
        try (SqlSession session = MybatisUtils.getSqlSession()) {
            TeacherMapper mapper = session.getMapper(TeacherMapper.class);
            Teacher teacher = mapper.getTeacherById(1);
            System.out.println(teacher);
        }
    }
}

输出结果为

Teacher(id=1, name=老师A, 
		studentList=[Student(id=1, name=小明, teacher=null), 	    
        Student(id=2, name=小红, teacher=null), 
        Student(id=3, name=小张, teacher=null), 
        Student(id=4, name=小李, teacher=null), 
        Student(id=5, name=小王, teacher=null)])

所以,实体类完全可以相互持有

6.5.2、按查询(子查询)

只需要修改 TeacherMapper.xml 为:

<mapper namespace="com.dzy.dao.TeacherMapper">

    <resultMap id="teacherMap" type="Teacher">
        <id property="id" column="id" />
        <result property="name" column="name"/>
        <!-- javaType是集合的类型,ofType是集合里的元素的类型 -->
        <!-- colunm是传入参数的列名,绝对不能错 -->
        <collection property="studentList" column="id" javaType="ArrayList" ofType="Student" select="getStudentByTid"/>
    </resultMap>

    <select id="getTeacherById" resultMap="teacherMap">
        select * from teacher where id = #{tid}
    </select>
    
    <select id="getStudentByTid" parameterType="int" resultType="Student">
        select * from student where tid = #{tid}
    </select>
</mapper>

返回结果是一样的

一般选择**按结果(联表)**的方法,因为只有一个 sql 方便调试。

6.6、小结

  • association & collection
  • javaType & ofType
  • property & column
  • 多个 sql & 一个 sql

高频问题:

  • MySQL 引擎
  • InnoDB 底层原理
  • 索引
  • 索引优化
发布了41 篇原创文章 · 获赞 2 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/dzydzy7/article/details/105018164