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 }
View Code

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');
View Code

 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 }
View Code

【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>
View Code

【说明】

①当传递过来的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 }
View Code

 

 方案二:按结果嵌套处理【推荐使用】

思路:直接使用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 }
View Code

【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     }
View Code

 

  方案二:按查询嵌套处理

此时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,通过日志可以用来分析运行是出现的错误;

 

(未完待续...)

posted on 2020-12-09 22:59  jyf上善若水  阅读(299)  评论(0)    收藏  举报