Mybatis关联映射

  Mybatis是半自动化的ORM框架,相比于Hibernate具有更好的灵活性,更容易进行性能优化,当然Hibernate和Mybatis各具特点,并不存在技术的优劣问题,只是应用场景不同,对于一个优秀的开发人员来说最好二者的技术都能掌握。Mybatis需要程序员完成实体类属性和数据库表字段之间的映射设计,并可以定制化返回类型,因此具有更高的灵活性,设计数据库表间的关联映射是Mybatis的核心,本文主要描述Mybatis进行表间关联映射设计的基本内容,包括完成一对一、一对多、多对多常见关联关系的映射。

一 案例数据模型

    采用案例驱动的方式去描述、验证问题往往可以加深技术原理和机制的理解。现模拟日常生活中最常见的学生-课程-选课关联关系,为更好的描述一对一关联再添加学生与学生证一对一关联,其中学生与课程之间为多对多关系,学生与选课之间为一对多关系,课程与选课之间为多对一关联,具体的数据模型关系图如下:

  需要注意的是,上图标注学生选课表与课程之间为一对一关联可能会令人困惑,但其表示当学生ID和课程ID确定时,课程也随之唯一确定,所以选课和课程之间为一对一关联,反之为多对一关联

二 一对一关联

  一对一是最简单的关联关系,本例中学生和学生证即为一对一的关系,一个学生唯一对应一个学生证,反之由学生证也能唯一确定一个学生。创建Student、StudentSelfCard实体类

package com.learn.mybatis.entity;
import java.util.List;
/**
 * 学生POJO
 * Created by lfq on 2017/5/15.
 */
public class Student {
    private int id;
    private String name;
    private String sex;
    private String note;
    private StudentSelfCard studentSelfCard;//与学生证建立一对一关联
....省略Setter/Getter方法........
}
package com.learn.mybatis.entity;
import java.util.Date;
/**
 * 学生证POJO
 * Created by wyh on 2017/7/23.
 */
public class StudentSelfCard {
    private Integer id;//主键ID
    private String studentId;//与学生表建立一对一关联
    private String province;//籍贯
    private Date issueDate;//发证日期
    private Date endDate;//结束日期
    private String note;//备注
....省略Setter/Getter方法.....
}
  • Mapper接口

  Mybatis-Spring中为开发者提供了SqlSessionTempate,其封装了大量的CRUD方法,使数据的持久化操作变得更加简单,但本着面向接口编程的原则,我们采用定义映射器接口,并由Mybatis根据动态代理机制生成代理类完成相应操作,创建映射器非常简单,仅需提供一个接口即可,接口中包含数据持久化的方法,方法名对应配置文件中的SQL语句唯一标识。

package com.learn.mybatis.mapper;
import com.learn.mybatis.entity.Student;
/**
 * Created by lfq on 2017/5/15.
 */
public interface StudentDAO {
    Student getStudent(int id);
}
  •   XML映射配置文件

  本例中学生表为核心主表,以其为主,与其他表建立关联关系,其中Student表的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.learn.mybatis.mapper.StudentDAO">
    <!--嵌套结果查询-->
    <resultMap id="studentResult" type="com.learn.mybatis.entity.Student">
        <id property="id" column="student_id"/>
        <result property="name" column="student_name"/>
        <result property="sex" column="sex"/>
        <result property="note" column="student_note"/>
        <!--与学生证建立一对一关联-->
        <association property="studentSelfCard" column="student_id" javaType="com.learn.mybatis.entity.StudentSelfCard">
            <id property="id" column="card_id"/>
            <result property="province" column="native"/>
            <result property="issueDate" javaType="java.util.Date" jdbcType="DATE" column="issue_date"/>
            <result property="endDate" javaType="java.util.Date" jdbcType="DATE" column="end_date"/>
            <result property="note" column="card_note"/>
            <result property="studentId" column="student_id"/>
        </association>
<select id="getStudent" parameterType="int" resultMap="studentResultMap"> SELECT id,cname as name,sex,note FROM t_student WHERE id=#{id} </select> </mapper>

   由上当我们通过映射器调用getStudent方法是,便会执行select查询,并返回结果集,需要注意的是<association>表示有一个的意思,property表示实体类的属性,column表示查询关联的字段,当映射studentSelftCard属性时,会传入student_id作为参数去查询Student_SelfCard并返回结果集映射。

三 一对多关联

   一个学生可以拥有多个选课结果,则学生与选课表间为一对多关联,当学生和选课确定时,课程也随之确定,则选课与课程之间为一对一关联,新增StudentLecture和Lecture实体类

package com.learn.mybatis.entity;
/**
 * 学生选课POJO
 * Created by wyh on 2017/5/15.
 */
public class StudentLecture {
    private int id;
    private int studentId;//学生ID
    private Lecture lecture;//与课程表建立关联
    private String note;//备注
    private double score;//分数
.....省略Setter/Getter方法
}
package com.learn.mybatis.entity;
/**
 * 学生课程POJO
 * Created by wyh on 2017/5/15.
 */
public class Lecture {
    private int id;//主键ID
    private String lectureName;//课程名
    private String note;//备注
......省略Setter/Getter方法
}

  此时Student实体类中需要加上 private List<StudentLecture> studentLectureList; 与选课表建立一对多关联,此时XML映射配置文件对应需要<Collection>建立一对多关联,即配置文件需要添加集合映射,其中ofType表示集合中的元素类型。

 <!--与课程成绩表建立一对多关联-->
        <collection property="studentLectureList" column="student_id" ofType="com.learn.mybatis.entity.StudentLecture">
            <id property="id" column="stu_lecture_id"/>
            <result property="studentId" column="student_id"/>
            <result property="score" column="grade"/>
            <result property="note" column="stu_lecture_note"/>
            <!--与课程建立一对一关联-->
            <association property="lecture" javaType="com.learn.mybatis.entity.Lecture" column="lecture_id">
                <id property="id" column="lecture_id"/>
                <result property="lectureName" column="lecture_name"/>
                <result property="note" column="lecture_note"/>
            </association>
        </collection>

   多对多关联其实是双向的一对多关联,本质上可以分解为一对多关联,因此掌握一对多即可。

三 执行复杂查询映射

   Mybatis中支持复杂的查询结果集映射,这是其灵活性的重要体现,在进行结果集映射时具有嵌套语句和嵌套结果两种,前者会产生N+1的性能问题,故一般都采用嵌套结果的方式执行查询。嵌套结果时,尽量对SQL语句进行关联优化,因为过多的关联会降低查询性能,本例中展示查询所有学生的选课及课程信息,设计到Student、Student_SelfCard、Student_Lecture、Lecture四张表的关联查询,连接方式均为左外连接,则SQL语句配置如下:

  <select id="queryAllStudent" parameterType="Integer" resultMap="studentResult">
    SELECT
        stu.id AS student_id,
        stu.cname AS student_name,
        stu.sex,
        stu.note AS student_note,
        card.id AS card_id,
        native,
        issue_date,
        end_date,
        card.note AS card_note,
        stu_lec.id AS stu_lecture_id,
        stu_lec.grade,
        stu_lec.note AS stu_lecture_note,
        lec.id AS lecture_id,
        lec.lecture_name,
        lec.note AS lecture_note
    FROM
        t_student stu
    INNER JOIN t_student_selfcard card ON stu.id = card.student_id
    INNER JOIN t_student_lecture stu_lec ON stu_lec.student_id = stu.id
    INNER JOIN t_lecture lec ON lec.id = stu_lec.lecture_id
    WHERE stu.id =#{id};
    </select>

  注意当多表关联查询时,对于相同字段的列需要设置别名便于区分,通过执行以上SQL便可返回目标结果集。Mybaits通过设计SQL及结果集便可大大提高程序的灵活性,这也是广受开发者青睐的原因之一,因此需要熟练掌握。

 

posted @ 2017-07-26 23:30  One160701  阅读(1314)  评论(0编辑  收藏  举报