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及结果集便可大大提高程序的灵活性,这也是广受开发者青睐的原因之一,因此需要熟练掌握。