Mybatis框架学习(2)--结果集映射ResultMap
说明:本文档所需要解决的问题是:
1、实体类属性名和数据库表中字段名不一致的情形(一对一);
2、数据库实体间的一对多和多对一的情形。
一、数据库实体间一对一关系
1、环境搭建--参考Mybatis框架学习(1)
- 搭建实验所需的数据库和表
- 在pom.xml文件中导入Mybatis的相关依赖
- mybatis、mysql-connector-java(java连接MySql所需要的驱动)
- 编写Mybatis的核心配置文件--参考Mybatis的官方文档
- environments
- typeAliases
- setting
- properties
- mappers
- ...
- 编写Mybatis工具类
- 实体类以及操作数据库的Mapper接口--实体类中属性名称与数据库表中的字段名称不相同
- Mapper.xml配置文件的编写
2、单表查询详情
1、数据库中表中的字段名如下图所示:

2、java实体类的设计--实体类的属性与表中的字段名称不一致
1 public class Books { 2 3 private Integer id; 4 private String name; 5 private Integer counts; 6 private String detail; 7 8 public Books() { 9 } 10 11 public Books(Integer id, String name, Integer counts, String detail) { 12 this.id = id; 13 this.name = name; 14 this.counts = counts; 15 this.detail = detail; 16 } 17 18 public Integer getId() { 19 return id; 20 } 21 22 public void setId(Integer id) { 23 this.id = id; 24 } 25 26 public String getName() { 27 return name; 28 } 29 30 public void setName(String name) { 31 this.name = name; 32 } 33 34 public Integer getCounts() { 35 return counts; 36 } 37 38 public void setCounts(Integer counts) { 39 this.counts = counts; 40 } 41 42 public String getDetail() { 43 return detail; 44 } 45 46 public void setDetail(String detail) { 47 this.detail = detail; 48 } 49 50 @Override 51 public String toString() { 52 return "Books{" + 53 "id=" + id + 54 ", name='" + name + '\'' + 55 ", counts=" + counts + 56 ", detail='" + detail + '\'' + 57 '}'; 58 } 59 }
3、Mapper接口
1 public Books findBookByID(int id);
4、Mapper映射文件--未添加结果集映射
1 <select id="findBookByID" resultMap="Books" parameterType="int"> 2 select * from books where bookID = #{id} 3 </select>
①由于实体类的属性与表中的字段名称不一致,因此查询出的书籍id、name以及counts将为空;
②分析:
[1]select * from books where bookID = #{id}可以看做是 select bookID,bookName,bookCounts,detail where bookID = #{id};
[2]Mybatis会根据查询语句中的列名(数据库不区分大小写),去对应的实体类中找相应列名的set方法对属性进行设值,由于找不到setBookID,setBookName,setBookCounts,所以id、name以及counts的返回值将为空。
5、解决方案
方案一:
通过4中的结果分析知,可以采用表中的列名起别名的方式:别名和java实体类中的属性名称一致即可;
例如在本实例中,仅需将mapper映射文件中查询语句修改为
1 <select id="findBookByID" resultType="Books" parameterType="int"> 2 select bookId id,bookName name,bookCounts counts,detail from books where bookID = #{id} 3 </select>
方案二:【推荐使用】
使用结果集映射--resultMap,此时mapper映射文件的内容为:
1 <!--由于实体类的属性名和数据库表中的字段名不一致,需要使用resultMap标签进行--> 2 <resultMap id="BookMap" type="Books"> 3 <result property="id" column="bookID"></result> 4 <result property="name" column="bookName"></result> 5 <result property="counts" column="bookCounts"></result> 6 </resultMap> 7 8 <select id="findBookByID" resultMap="BookMap" parameterType="int"> 9 select * from books where bookID = #{id} 10 </select>
【说明】
①column对应数据库表中的列名,property对应实体类中的属性名;
②与原始映射文件相比,select标签中的resultType变为resultMap。
数据库实体之间除了简单的一对一关系外,还存在一对多或多对一的情形,此时我们需要使用一些高级的结果集映射来实现相应的查询功能。
二、数据库实体间一对多(/多对一)关系
1、多对一的处理
1、关于”多对一“的理解(以老师、学生之间的关系为例说明)
- 多个学生 vs 一个老师
- 如果从学生的角度出发,就是一个多对一的现象,即多个学生关联一个老师
2、数据库设计
1 -- 老师表 2 CREATE TABLE teacher( 3 id INT(10) PRIMARY KEY AUTO_INCREMENT, 4 NAME VARCHAR(30) DEFAULT NULL 5 ); 6 7 INSERT INTO teacher VALUES(1,'江老师'); 8 9 -- 学生表 10 CREATE TABLE student( 11 id INT(10) PRIMARY KEY AUTO_INCREMENT, 12 NAME VARCHAR(30) DEFAULT NULL, 13 tid INT(10), 14 CONSTRAINT fk_student_teacher FOREIGN KEY(tid) REFERENCES teacher(id) 15 ); 16 17 INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1'); 18 INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1'); 19 INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1'); 20 INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1'); 21 INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');

3、环境搭建
- 在pom.xml文件中mybatis的相关依赖和lombok
- 编写mybatis的核心配置文件
- 编写mybatis工具类
- 编写实体类及对应的Mapper接口
【实体类】由于是从学生的角度看问题,此时Teacher可以看做Student类的一个属性
1 import lombok.Data; 2 @Data 3 public class Student { 4 private int id; 5 private String name; 6 7 private Teacher teacher; 8 } 9 10 import lombok.Data; 11 @Data 12 public class Teacher { 13 private int id; 14 private String name; 15 }
【Mapper接口】
1 public interface StudentMapper { 2 public List<Student> selectAll(); 3 }
- 编写mapper接口对应的mapper.xml配置文件
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 6 <mapper namespace="com.yif.mapper.StudentMapper"> 7 8 </mapper>
4、要求:查询所有学生及对应老师的信息
方案一:按查询嵌套处理
需求:获取所有学生及对应老师的信息
分析:
1、获取所有学生的信息;
2、根据获取的学生信息的老师id-->获取老师的信息
2.1、做一个结果集映射:resultType=studentTeacher
2.2、studentTeacher结果集的类型为Student
2.3、在实体类Student中,老师的属性为teacher,对应数据库中的字段为tid
2.4、使用association来处理多对一的复杂关联
1 <select id="selectAll" resultMap="StudentTeacher"> 2 select * from student 3 </select> 4 5 <resultMap id="StudentTeacher" type="Student"> 6 <result property="id" column="id"></result> 7 <result property="name" column="name"></result> 8 <!--association关联属性 property属性名称 javaType属性类型 column在多的一方的表中的列名--> 9 <association property="teacher" column="tid" javaType="Teacher" select="selectByID"></association> 10 </resultMap> 11 12 13 <select id="selectByID" resultType="Teacher"> 14 select * from teacher where id = #{tid} 15 </select>
【说明】
①当传递过来的id(即association中的column)只有一个属性的时候,此时查询teacher表中信息的#{id}可以任意写;
②当association中column有多个参数配置时:column="{k1= v1,k2=v2}",此时k就是传给下个sql(查询teacher表)取值的名称,v是student表中的字段名。
例如:
... <association ptoperty="teacher" column="{id=tid,name=tname}" javaType="Teachenr" select="getTeacher"> ... <select id="getTeacher" resultType="Teacher"> select * from teacher where id = #{id},name=#{name} </select>
【测试以及查询结果】
1 public void test1(){ 2 SqlSession sqlSession = MybatisUtils.getSqlSession(); 3 StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); 4 List<Student> studentList = studentMapper.selectAll(); 5 for (Student student : studentList) { 6 System.out.println(student); 7 } 8 sqlSession.close(); 9 }

方案二:按结果嵌套处理【推荐使用】
思路:直接使用sql语句查询出结果,然后进行结果集的映射
此时mapper.xml配置的如下:
1 <!--使用结果集嵌套完成多表查询--> 2 <select id="selectAll" resultMap="StudentTeacher"> 3 select s.id sid, s.name sname, t.name tname 4 from student s, teacher t 5 where s.tid = t.id 6 </select> 7 8 <resultMap id="StudentTeacher" type="Student"> 9 <result property="id" column="sid"></result> 10 <result property="name" column="sname"></result> 11 <association property="teacher" javaType="Teacher"> 12 <result property="name" column="tname"></result> 13 </association> 14 </resultMap>
【说明】
①此时的property是Student实体类中的属性,而column则是student和teacher表中字段名起别名后名称
2、一对多的处理
1、关于”一对多“的理解(以老师、学生之间的关系为例说明)
- 多个学生 vs 一个老师
- 如果从老师的角度出发,就是一个一对多的现象,即一个老师下面集合多个学生
2、环境搭建与“多对一”的相同
3、实体类及Mapper接口的编写
【实体类】
1 @Data 2 public class Student { 3 private int id; 4 private String name; 5 private int tid; 6 } 7 8 @Data 9 public class Teacher { 10 private int id; 11 private String name; 12 13 List<Student> students; 14 }
【Mapper接口】
1 public interface TeacherMapper { 2 3 public Teacher findTeacherByID(@Param("tid") int id); 4 5 }
4、要求:获取指定老师及其所有学生信息
方案一:按结果嵌套处理【推荐使用】
1 <!-- 2 方式一:使用结果嵌套完成一对多的查询 3 思路: 4 1、从学生表和老师表中查询出学生id、学生姓名和老师姓名 5 2、对查询出来的操作做结果集映射 6 2.1、集合使用collection,其中, 7 JavaType和ofType都是用来指定对象的类型,其中 8 JavaType用来指定pojo中属性的类型,ofType指定的是映射到List属性中pojo的类型 9 --> 10 11 <select id="findTeacherByID" resultMap="TeacherStudent" parameterType="int"> 12 select s.id sid, s.name sname, t.name tname, t.id tid 13 from student s, teacher t 14 where s.tid = tid and tid = #{tid} 15 </select> 16 <!--TeacherStudent--> 17 <resultMap id="TeacherStudent" type="Teacher"> 18 <result property="id" column="tid"></result> 19 <result property="name" column="tname"></result> 20 <collection property="students" ofType="Student"> 21 <result property="id" column="sid"></result> 22 <result property="name" column="sname"></result> 23 <result property="tid" column="tid"></result> 24 </collection> 25 </resultMap>
【说明】
①此时的property是Teacher实体类中的属性,而column则是student和teacher表中字段名起别名后名称
【测试以及查询结果】
1 public void test(){ 2 SqlSession sqlSession = MybatisUtils.getSqlSession(); 3 TeacherMapper teacherMapper = sqlSession.getMapper(TeacherMapper.class); 4 Teacher teacher = teacherMapper.findTeacherByID(1); 5 System.out.println(teacher); 6 sqlSession.close(); 7 }

方案二:按查询嵌套处理
此时mapper.xml配置的如下:
1 <!--方式二:使用查询嵌套完成一对多的查询--> 2 <select id="findTeacherByID" resultMap="TeacherStudent" parameterType="int"> 3 select * from teacher where id = #{tid} 4 </select> 5 6 <resultMap id="TeacherStudent" type="Teacher"> 7 <result property="id" column="id"></result> 8 <result property="name" column="name"></result> 9 <!--column是一对多的外键,写的是“一”的主键列名--> 10 <collection property="students" javaType="ArrayList" ofType="Student" column="id" select="selectStudent"></collection> 11 </resultMap> 12 13 <select id="selectStudent" resultType="Student"> 14 select * from student where tid = #{id} 15 </select>
三、小结
1、关联:association(多对一)
2、集合:collection(一对多)
3、javaType和ofType都是用来指定对象的类型
- javaType用来指定pojo中属性的类型
- ofType指定的是映射到list集合属性中的pojo类型
4、注意事项:
- 注意实体类属性名与数据库表字段名不一致的情形;
- 注意一对多/多对一中字段和属性的对应问题:查询嵌套处理和结果嵌套处理(推荐);
- 尽量使用Log4j,通过日志可以用来分析运行是出现的错误;
(未完待续...)
浙公网安备 33010602011771号