美文网首页开源工具技巧程序员
自学Mybatis系列(4)——ResultMap结果映射器

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

作者: AceCream佳 | 来源:发表于2017-02-19 19:10 被阅读0次

    写在前面:十分感谢《深入浅出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来测试
    当我们获取到一名学生的时候,同时获得了他的一堆学生课程,通过学生课程们,再一一对应各门课。
    当我们获取某一门课的时候,同时获得了这门课的一堆学生课程,通过学生课程们,再一一对应各位学生。
    自此结束

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

    相关文章

      网友评论

        本文标题:自学Mybatis系列(4)——ResultMap结果映射器

        本文链接:https://www.haomeiwen.com/subject/kyedwttx.html