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及結果集便可大大提高程序的靈活性,這也是廣受開發者青睞的原因之一,因此需要熟練掌握。