开源工具技巧程序员

自学Mybatis系列(4)——ResultMap结果映射器

2017-02-19  本文已影响0人  AceCream佳

写在前面:十分感谢《深入浅出Mybatis技术原理与实战》这本书,大多数部分是书上的话,希望自己能在后面的文章中多写一些自己的理解。而且最重要的是!每次照书无脑敲的时候,都好羞愧啊(害羞脸)。后面一定注意这些问题。最后要感谢点赞、评论以及指正的朋友们,你们是坠吼的!!!


官方文档有云:

The resultMap element is the most important and powerful element in MyBatis.
resultMap元素是Mybatis中最重要也是最强大的元素。

作用是:定义映射规则、级联的更新、定制类型转换器。
其实我也是刚刚熟悉它,对它的使用并没有达到炉火纯青的地步,毕竟这个元素还是很复杂的。我只是把自己的理解过程记录下来,如果有错误,希望大家指正!


先说说他的结构:

<resultMap>
    <constructor>
        <idArg/>
        <arg/>
    </constructor>
    <id/>
    <result/>
    <association>
    <collection/>
    <discriminator>
        <case/>
    </discriminator>
</resultMap>

眼花缭乱对吧!这里一点一点讲解:
constructor:用于配置构造方法。这里打个比方,当我们的实体类存在没有参数的构造方法时候。就不需要去配置它!比如我们有个Student类参数有id和name。

class Student{
    private int id;
    private String name; 
}

熟悉java基础的同学知道,如果我们不去书写构造方法,那么java会自动帮我们建立一个无参数的构造方法:public Student(){}
但是如果我们写了带有参数的构造方法,这个无参构造方法将不会被自动创建!
所以对于这个构造方法:

public Student(int id,String name){
      this.id = id;
      this.name = name;
}

我们需要配置结果集的constructor:

    <constructor>
        <idArg column="id" javaType="int" />
        <arg column="name" javaType="string"/>
        <arg/>
    </constructor>
  .....
</resultMap>

也就代表着当我们不去写带参构造器的时候就不用去管constructor这个标签。如此即可,是不是理解起来很简单!


id和result
id表示主键的列,允许多个主键,如果是多个的话,就是联合主键。
result配置的是实体类和数据库中列名的映射关系。

我们先不说后面的级联。先研究研究如何储存结果集。
储存结果集有两种方式:1.map 2.pojo(实体类)
但是用map这个方式说实话比较low。阅读性很差,所以pass。
我们使用POJO来存,好处是我们可以利用自动映射,还可以用select中的resultMap属性去配置映射集合。
举个小例子:
假设我们数据库中t_student表有两个字段:student_id和student_name。
我们POJO类中Student类有两个变量:studentId和studentName

我们的select语句如果这么写:(前提是其他文件已经写好)

<select id="getStudents">
    select student_id,student_name from t_student
</select>

这样会发现我们获取到的ID号和姓名是空值,因为字段和变量名不是一一对应的。
我们需要这么写resultMap

<result id="studentResultMap" type="com.cream.pojo.Student">
    <id  property="studentId" column="student_id/>
    <result property="studentName" column="student_name"/>
</result>

此时我们需要在select标签中加上 resultMap="studentResultMap" 即可配置成功。
数据也就可以直接取出来了!这几段我是凭自己记忆写的,之前测试的类被我删掉了,所以大致写个样子,只是为了一看就能马上回忆起来思路!


级联

接下来要说级联了,心里挺没底气的,毕竟是今天早上刚有点开窍,这东西真的是光看是很难理解的,只有自己动手敲一敲再想一想才能真正的悟出来!
Mybatis中的级联有三种:association、collection和discriminator。
discriminator很复杂,先研究完
级联呢,分为三种对应关系:
1.一对一
所谓一对一,打个比方:学生和学生证的关系。一名学生对应一张学生证。这个用association。
2.一对多
所谓一对多,打个比方:公司和员工,一家公司拥有很多员工。这个用collection。
3.多对多
所谓多对多,这个挺烦的:学生和课程。一名学生有很多课程。一门课程,有很多学生!那这个怎么办呢?比如说我们一般再建一张表学生课程表,专门存这个关系,双向使用一对多。达到多对多的关系。当然我们可以向这个表加一些比如说成绩之类的字段精确的存数据。
说了很多,估计看到这里一头雾水。接下来用实例讲解一下。


一对一

书上的例子很好:比如说学生和学生证,他们是一对一的关系。
那么当我们想要通过学生的id号查询到学生的时候,同时取得他的学生证信息,我们应该怎么设计呢?
思路应该是:把学生证(StudentSelfcard)这个类,当做类型放到学生类(Student)中去。
所以建学生类POJO的时候我们需要这么写:
学生类:

public class Student {
    private StudentSelfcard studentSelfcard;  //学生证类在学生类其中
    ...其他参数...
    ......getter and setter方法......  
}

接下来,比如我们要通过学号取查询学生。那么Mapper应该这么写:
StudentSelfcardMapper.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 namespace="com.cream.dao.StudentSelfcardDao">
    <resultMap id="studentSelfacrdMap" type="com.cream.pojo.StudentSelfcard">
        <id property="id" column="id"/>
        <result property="studentId" column="student_id"/>
        <result property="native_" column="native"/>
        <result property="issueDate" column="issue_date"/>
        <result property="endDate" column="end_date"/>
        <result property="note" column="note"/>
    </resultMap>
    <select id="findStudentSelfcardByStudentId" resultMap="studentSelfacrdMap">
        SELECT id,student_id,native,issue_date,end_date,note
        FROM t_student_selfcard WHERE student_id = #{studentId }
    </select>
</mapper>

StudentMapper.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 namespace="com.cream.dao.StudentDao">
    <resultMap id="studentMap" type="com.cream.pojo.Student">
        <id property="studentId" column="student_id"/>
        <result property="cnname" column="cnname"/>
        <result property="sex" column="sex"/>
        <result property="note" column="note"/>
        <association property="studentSelfcard" column="student_id"
                     select="com.cream.dao.StudentSelfcardDao.findStudentSelfcardByStudentId"/>
    </resultMap>

    <select id="getStudent" parameterType="int" resultMap="studentMap">
        SELECT student_id,cnname,sex,note FROM  t_student WHERE student_id = #{studentId }
    </select>
</mapper>

关于上面的association讲解一下属性:
property: 映射到结果上的属性,其实就是我们通过关联又获取到了什么类型。
column:我们传送了什么参数过去,这里是student_id那传过去的就是它。
select:代表着由哪条SQL去查询。这里写的是接口以及里面的方法哦,不是xml文件。
在main函数中调用接口和方法即可知道,当我们确定的获取到一个学生信息的时候,同时我们获得了该学生的学生证信息。

一对多

这里想通过多对多去说,所以一对多不想说的太麻烦,先思考,当一对一的时候,是学生中包含着学生证。那么一对多可以用相同的思路来考虑:学生和成绩。一名学生对应着很多成绩,而成绩对应着一名学生。此时需要在学生类的中设置类型为List<成绩>的参数。从而达到,我获取到了一名学生的信息,同时我获取到了一个list,里面放的是这个学生的所有成绩信息。

多对多

其实现实生活中多对多的例子并不多,但我感觉有时候寻思着就把自己带坑里去了,这样来想:学生和课程。一名学生有多门课程,同时一门课程有很多学生去学。这里直接建立联系就很尴尬!
推荐一个方式,建立中间的表。这个表中可以存放学生学号,课程编号,和此学生这门课程的其他信息,比如说老师或者成绩,当然这都是后话了,只需要看这个例子就好。(本人在POJO中把其他无关的地方省略了)
三个POJO:

public class Student {
    private StudentSelfcard studentSelfcard;  //学生证类在学生类其中
    private List<StudentLecture> studentLectureList;
    ...其他参数...
    ......getter and setter方法......  
}
public class Lecture {
    private List<StudentLecture> studentLectureList;
    ...其他参数...
    ......getter and setter方法......  
}
public class StudentLecture {
    private Lecture lecture;
    private Student student;    
      ...其他参数...
    ......getter and setter方法......  
}

三个Mapper.xml,这里就不客气的全粘了:
StudentMapper.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 namespace="com.cream.dao.StudentDao">
    <resultMap id="studentMap" type="com.cream.pojo.Student">
        <id property="studentId" column="student_id"/>
        <result property="cnname" column="cnname"/>
        <result property="sex" column="sex"/>
        <result property="note" column="note"/>
        <association property="studentSelfcard" column="student_id"
                     select="com.cream.dao.StudentSelfcardDao.findStudentSelfcardByStudentId"/>
        <collection property="studentLectureList" column="student_id"
                    select="com.cream.dao.StudentLectureDao.findStudentLectureByStuId"/>

    </resultMap>
    <select id="getStudent" parameterType="int" resultMap="studentMap">
        SELECT student_id,cnname,sex,note FROM  t_student WHERE student_id = #{studentId }
    </select>

</mapper>

LectureMapper.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 namespace="com.cream.dao.LectureDao">
    <resultMap id="LectureMap" type="com.cream.pojo.Lecture">
        <id property="courseId" column="course_id"/>
        <result property="lectureName" column="lecture_name"/>
        <result property="note" column="note"/>
        <collection property="studentLectureList" column="lecture_id"
                    select="com.cream.dao.StudentLectureDao.findStudentLectureByLecId"/>
    </resultMap>
    <select id="getLecture" resultMap="LectureMap">
        SELECT lecture_id,lecture_name,note FROM t_lecture WHERE lecture_id = #{lectureId }
    </select>

</mapper>

StudentLectureMapper.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 namespace="com.cream.dao.StudentLectureDao">
    <resultMap id="studentLectureMap" type="com.cream.pojo.StudentLecture">
        <id property="id" column="id"/>
        <result property="studentId" column="student_id"/>
        <result property="lectureId" column="lecture_id"/>
        <result property="grade" column="grade"/>
        <result property="note" column="note"/>
        <association property="lecture" column="lecture_id"
                     select="com.cream.dao.LectureDao.getLecture"/>
        <association property="student" column="student_id"
                     select="com.cream.dao.StudentDao.getStudent"/>

    </resultMap>
    <select id="findStudentLectureByStuId" parameterType="int" resultMap="studentLectureMap">
        SELECT id,student_id,lecture_id,grade,note FROM t_student_lecture WHERE student_id = #{studentId }
    </select>

    <select id="findStudentLectureByLecId" parameterType="int" resultMap="studentLectureMap">
        SELECT id,student_id,lecture_id,grade,note FROM t_student_lecture WHERE lecture_id = #{LectureId }
    </select>

</mapper>

然后通过main来测试
当我们获取到一名学生的时候,同时获得了他的一堆学生课程,通过学生课程们,再一一对应各门课。
当我们获取某一门课的时候,同时获得了这门课的一堆学生课程,通过学生课程们,再一一对应各位学生。
自此结束

总结一下这篇文章:
写的有些凌乱,很多地方自己都快绕蒙了。我的数据库能力真的很生涩。在设计表与表之间的关系思路上也是十分不成熟的。这篇自认为写的也比较一般。希望自己能够在练习中提高。

上一篇下一篇

猜你喜欢

热点阅读