【MyBatis】(3)xml 映射文件

前言

該篇文章爲 xml 映射文件的筆記,主要講 xml 映射文件(也就是xxxMapper.xml 文件)。

一、mapper(xml文件)

MyBatis 的真正強大在於它的語句映射,這是它的魔力所在。由於它的異常強大,映射器的 XML 文件就顯得相對簡單。如果拿它跟具有相同功能的 JDBC 代碼進行對比,你會立即發現省掉了將近 95% 的代碼。MyBatis 致力於減少使用成本,讓用戶能更專注於 SQL 代碼。

SQL 映射文件(xml 映射文件)僅有幾個頂級元素,按照應被定義的順序列出。

  • cache:該命名空間的緩存配置。
  • cache-ref:引用其它命名空間的緩存配置。
  • resultMap:描述如何從數據庫結果集中加載對象,是最複雜也是最強大的元素。
  • sql:可被其它語句引用的可重用語句塊。
  • insert:映射插入語句。
  • update:映射更新語句。
  • delete:映射刪除語句。
  • select:映射查詢語句。

上面就是這幾個就是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.shengjava.mapper.IUserMapper" >
    <select id="selectUserById" resultType="com.shengjava.pojo.User" parameterType="int">
      select * from User where id = #{id}
    </select>
</mapper>

簡單解釋一下上面的映射文件文件:

  • <?xml> 標籤:第一行的是XML的聲明,其中version屬性是必須寫的。
  • 標籤:是一種標準通用標記語言的文檔類型聲明,它的目的是要告訴標準通用標記語言解析器它應該使用什麼樣的文檔類型定義(DTD)來解析文檔。
  • 標籤:其他標籤的父標籤。namespace屬性:映射文件對應的接口全限定路徑(推薦這樣做,可以查看官網的“探究已映射的 SQL 語句”下的“提示 對命名空間的一點補充”)。

  • 標籤:查詢語句標籤。

1.select 元素(查詢)

select 元素用於寫查詢查詢語句,標籤中共有十三個屬性。屬性設置很多,但是主要的頁沒幾個。詳細的屬性請參考官方文檔

舉例:如下就是一個簡單的根據用戶id查詢用戶的sql語句。

    <select id="selUser" parameterType="int" resultMap="userResultMap">
      select * from user where user_id = #{id}
    </select>

注意:這邊案例的返回值屬性resultMap值爲userResultMap,這個值爲標籤中的id,會在下面提到

常用屬性:

屬性值 描述
id 接口中的方法名
parameterType 傳入的參數類型,可以不寫。
resultType 返回結果的類全限定名或別名。(resultType 和 resultMap 之間只能同時使用一個。)
resultMap 對外部 resultMap 的命名引用。可以在 resultMap 標籤元素中建立映射。

2.insert, update 和 delete 元素

DML 語句都非常類似。屬性也和select元素屬性差不多。

舉例:增刪改

    <insert id="insUser" parameterType="User">
        insert into user(user_name, user_password, user_age, user_phone, user_email)
        values (#{name }, #{password}, #{age}, #{phone}, #{email});
    </insert>
    
    <delete id="delUser" parameterType="int">
        delete from user where user_id = #{id};
    </delete>

    <update id="updUser" parameterType="User">
        update user
        set user_name=#{name},user_password=#{password},user_age=#{age},user_phone=#{phone},user_email=#{email}
        where user_id=#{id};
    </update>

注意:這裏參數parameterType屬性值User是實體類,這裏我可以直接寫User的原因是因爲我在xml配置文件(也就是mybatis-config.xml)中配置了別名。也就是在mybatis-config.xml配置文件中的如下這段配置:

    <!-- 給pojo包下類取別名 -->
    <typeAliases>
        <typeAlias alias="User" type="com.shengjava.web.pojo.User"/>
    </typeAliases>

3.sql 元素

這個元素可以用來定義可重用的 SQL 代碼片段,以便在其它語句中使用。

<sql id="userColumns"> 這裏寫sql語句 </sql>

可以使用include標籤,在refid屬性引用sql代碼片段的id。

<include refid="userColumns"></include>,

4.resultMap 元素

resultMap 元素是 MyBatis 中最重要最強大的元素。它可以讓你從 90% 的 JDBC ResultSets 數據提取代碼中解放出來,並在一些情形下允許你進行一些 JDBC 不支持的操作。實際上,在爲一些比如連接的複雜語句編寫映射代碼的時候,一份 resultMap 能夠代替實現同等功能的數千行代碼。ResultMap 的設計思想是,對簡單的語句做到零配置,對於複雜一點的語句,只需要描述語句之間的關係就行了。

4.1 常用

resultMap 元素是 MyBatis中最重要最強大的元素。可以建立Java對象屬性property與數據庫字段column間的映射關係。這也是解決列名不匹配的另外一種方式。(用於java對象屬性和數據庫字段名字不匹配時)

舉例:
一個java實體類

public class User {
    private int id;
    private String name;
    private String password;
    private int age;
    private String phone;
    private String email;
    // 省略get/set
}

結果集元素resultMap,用於建立映射關係。

備註:標籤用於主鍵的映射(property值爲java對象屬性名稱,column值爲數據庫字段名稱),同理標籤用於其他java對象屬性與數據字段的映射。

    <resultMap id="userResultMap" type="User">
        <id property="id" column="user_id" />
        <result property="name" column="user_name"/>
        <result property="password" column="user_password"/>
        <result property="age" column="user_age"/>
        <result property="phone" column="user_phone"/>
        <result property="email" column="user_email"/>
    </resultMap>

上面定義好了結果集,現在可以在查詢返回的結果集中使用。例如:resultMap=“resultMap標籤中的id”。

    <select id="selUser" parameterType="int" resultMap="userResultMap">
      select * from user where user_id = #{id}
    </select>

備註:查詢結果爲null,說明POJO中的類屬性名稱和數據庫字段的不一致,

4.2 高級結果映射

resultMap結果集映射元素還包含多個標籤,都非常有用。

例如:

  • constructor - 用於在實例化類時,注入結果到構造方法中
  • association – 一個複雜類型的關聯;許多結果將包裝成這種類型
  • collection – 一個複雜類型的集合
  • discriminator – 使用結果值來決定使用哪個 resultMap

4.高級結果映射(補充)

resultMap 元素有很多子元素和一個值得深入探討的結構。 下面是resultMap 元素的概念視圖。

下表元素來自官網目錄“結果映射” 下的下的 “結果映射(resultMap)”

這些元素標籤是標籤的子元素,和標籤上面已經使用了,這這邊不在過多贅述(詳解點擊看官網“id & result”小節)。

  • constructor - 用於在實例化類時,注入結果到構造方法中
    • idArg - ID 參數;標記出作爲 ID 的結果可以幫助提高整體性能
    • arg - 將被注入到構造方法的一個普通結果
  • id – 一個 ID 結果;標記出作爲 ID 的結果可以幫助提高整體性能
  • result – 注入到字段或 JavaBean 屬性的普通結果
  • association – 一個複雜類型的關聯;許多結果將包裝成這種類型
    • 嵌套結果映射 – 關聯可以是 resultMap 元素,或是對其它結果映射的引用
  • collection – 一個複雜類型的集合
    • 嵌套結果映射 – 集合可以是 resultMap 元素,或是對其它結果映射的引用
  • discriminator – 使用結果值來決定使用哪個 resultMap
    • case – 基於某些值的結果映射
      • 嵌套結果映射 – case 也是一個結果映射,因此具有相同的結構和元素;或者引用其它的結果映射

4.1 association(關聯)(多對一處理)

  • association – 一個複雜類型的關聯;許多結果將包裝成這種類型
    • 嵌套結果映射 – 關聯可以是 resultMap 元素,或是對其它結果映射的引用

關聯(association)元素處理“有一個”類型的關係。 (多對一關係)

MyBatis 有兩種不同的方式加載關聯:

  • 嵌套 Select 查詢:通過執行另外一個 SQL 映射語句來加載期望的複雜類型。
  • 嵌套結果映射:使用嵌套的結果映射來處理連接結果的重複子集。

多對一關係:例如,對於學生,多個學生有一個老師,這就是多對一關係。

備註:該小節內容介紹可以查看官網 “結果映射” 下的 “關聯

4.1.1 關聯的嵌套 Select 查詢(類似子查詢)

需求:查詢學生所有信息(包括學生的教師)。(關聯查詢,多對一關係)

實體類:Student和Teacher類。學生類Student中包含一個教師屬性,用於存放學生的教師的信息。

@Data
public class Student {
    private int id;
    private String name;
    private int tId;
    private Teacher teacher;
}
@Data
public class Teacher {
    private int id;
    private String name;
}

StudentMapper.xml文件

主要查看resultMap標籤中的 association標籤屬性 。標籤上有各個屬性的解釋。更多詳細的解釋需要查看官網“結果映射”下的“關聯的嵌套 Select 查詢”

association標籤使用的是“關聯的嵌套 Select 查詢”,就是在該關聯中,使用了另一個查詢語句(兩條sql命令)。(這是第一種方式,還有另一種方式實現)

這種方式類似 子查詢 ,先查詢出一個(教師)再查詢另一個(學生),類似sql語句 :
select * from student where student.t_id = (select teacher.id from teacher) (注意,這條語句需要加上limit x,1才能成功查詢)

    <!--
        需求:查詢學生所有信息(包括學生的教師)
        思路:先查詢所有學生信息,再用學生的ti_d查詢教師信息
        對應的sql語句:select * from student s, teacher t where s.t_id = t.id
     -->
    <select id="selStudents" resultMap="StudentResultMap">
      select * from student
    </select>
    <select id="selTeacherById" parameterType="int" resultType="Teacher">
      select * from teacher where id = #{id}
    </select>
    <!-- 結果映射 -->
    <resultMap id="StudentResultMap" type="Student">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="tId" column="t_id"/>
        <!-- property:java屬性名。javaType:java屬性的類型(可以填:Java 類的完全限定名,或類型別名)
             column:數據庫中的列名,或者是列的別名。select:用於加載複雜類型屬性的映射語句的 ID -->
        <association property="teacher" javaType="Teacher" column="t_id" select="selTeacherById"/>
    </resultMap>

測試代碼:

public class StudentTest {
    @Test
    public void sel() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 從 SqlSessionFactory 中獲取 SqlSession
        try (SqlSession session = sqlSessionFactory.openSession()) {
            StudentMapper mapper = session.getMapper(StudentMapper.class);
            List<Student> students = mapper.selStudents();
            System.out.println(students);
        }
    }
}

輸出:(爲了排版好看一點,手動將輸出的list換行了)

[Student(id=1, name=小明, tId=1, teacher=Teacher(id=1, name=小明)), 
Student(id=2, name=小紅, tId=1, teacher=Teacher(id=2, name=小紅)), 
Student(id=3, name=小張, tId=1, teacher=Teacher(id=3, name=小張)), 
Student(id=4, name=小李, tId=2, teacher=Teacher(id=4, name=小李)), 
Student(id=5, name=小王, tId=2, teacher=Teacher(id=5, name=小王))]

備註:該小節內容可以查看 “結果映射” 官網下的 “關聯的嵌套 Select 查詢

4.1.2 關聯的嵌套結果映射(類似連表查詢)

另一種方式實現上面的查詢(使用“關聯的嵌套結果映射”)。

使用這種方式僅需要寫一條sql命令,個人覺得比較簡單,而且結構也比較清楚。只需要在標籤下將屬性的屬性名與數據庫字段名(可以是數據庫字段名也可以是別名)匹配好就行。

這種方式類似 連表查詢。一條語句將兩個表關聯起來直接查詢了。

    <!-- 方式二:關聯的嵌套結果映射 -->
    <select id="selStudents" resultMap="StudentResultMap">
      select s.id sid, s.name sname, s.t_id stid, t.id tid, t.name tname
      from student s, teacher t where s.t_id = t.id
    </select>
    <!-- 結果映射 -->
    <resultMap id="StudentResultMap" type="Student">
        <id property="id" column="sid"/>
        <result property="name" column="sname"/>
        <result property="tId" column="stid"/>
        <!-- 查詢語句使用正常的查詢語句(取了別名),再用association進行映射 -->
        <association property="teacher" javaType="Teacher">
            <id property="id" column="tid"/>
            <result property="name" column="tname"/>
        </association>
    </resultMap>

注意:如果兩個表的列名相同,需要取別名。如果需要關聯的表屬性太多,標籤中就會寫很多的和。此時可以在標籤中加上屬性resultMap=""。用於引用外部的結果集。官方文檔的案例中使用的就是該方式。

重點注意:

非常重要: id 元素在嵌套結果映射中扮演着非常重要的角色。你應該總是指定一個或多個可以唯一標識結果的屬性。 雖然,即使不指定這個屬性,MyBatis 仍然可以工作,但是會產生嚴重的性能問題。 只需要指定可以唯一標識結果的最少屬性。顯然,你可以選擇主鍵(複合主鍵也可以)。

備註:該小節內容可以查看 “結果映射” 官網下的 “關聯的嵌套結果映射

4.2 collection (集合)(一對多處理)

集合元素和關聯元素幾乎是一樣的。就不在提相同之處了,主要關注不同處。

一對多關係:例如,對於老師而言,一個教師有多個學生。

4.2.1 集合的嵌套 Select 查詢(子查詢)

實體類:還是使用上面的兩個實體類,不過需要注意,需要教師類中添加List類型的屬性 students。

@Data
public class Teacher {
    private int id;
    private String name;
    /** 教師的學生們 */
    private List<Student> students;
}

TeahcerMapper.xml映射文件。這裏面除了標籤名不同外,還增加了一個ofType屬性,這個主要用於“它用來將 JavaBean(或字段)屬性的類型和集合存儲的類型區分開來。”也就是泛型中的約束類型。

這種方式類似 子查詢 ,先查詢出一個(教師)再查詢另一個(學生),類似sql語句 : select * from student where student.t_id = x (x爲teacher表的id值)

    <!-- 第一種方式:集合的嵌套 Select 查詢(子查詢) -->
    <select id="selTeacher" parameterType="int" resultMap="TeacherResultMap">
      select * from teacher where id = #{id}
    </select>
    <select id="selStudentByTid" parameterType="int" resultType="Student">
      select * from student where t_id = #{id}
    </select>

    <resultMap id="TeacherResultMap" type="Teacher">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <!-- property:java屬性。javaType:屬性類型(可以不加)。ofType:java屬性類型的泛型類型。column:數據庫中的列名,或者是列的別名。select:用於加載複雜類型屬性的映射語句的 ID -->
        <collection property="students" javaType="list" ofType="Student" column="t_id"  select="selStudentByTid"/>
    </resultMap>

測試:

    @Test
    public void sel() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 從 SqlSessionFactory 中獲取 SqlSession
        try (SqlSession session = sqlSessionFactory.openSession()) {
            TeacherMapper mapper = session.getMapper(TeacherMapper.class);
            Teacher teacher = mapper.selTeacher(1);
            System.out.println(teacher);
        }
    }

輸出:(備註,學生中的teacher=null是因爲我沒有去查)

Teacher(id=1, name=秦老師, students=[Student(id=1, name=小明, tId=0, teacher=null), Student(id=2, name=小紅, tId=0, teacher=null), Student(id=3, name=小張, tId=0, teacher=null)])

備註:該小節內容可以查看 “結果映射” 官網下的 “集合的嵌套 Select 查詢”

4.2.2 集合的嵌套結果映射(連表查詢)

這個方式類似上面標籤的“集合的嵌套結果映射”,需要關注的是ofType屬性。

    <!--第二種方式:集合的嵌套結果映射(連表查詢)-->
    <select id="selTeacher" parameterType="int" resultMap="TeacherResultMap">
      select t.id tid, t.name tname, s.id sid, s.name sname, s.t_id stid
      from teacher t, student s where s.t_id=t.id and t.id = #{id}
    </select>
    <resultMap id="TeacherResultMap" type="Teacher">
        <id property="id" column="tid"/>
        <result property="name" column="tname"/>
        <collection property="students" ofType="Student">
            <id property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tId" column="stid"/>
        </collection>
    </resultMap>

備註:該小節內容可以查看 “結果映射” 官網下的 “集合的嵌套結果映射”

參考

官方文檔:XMl 映射文件


相關

我的該分類的其他相關文章,請點擊:【Spring + Spring MVC + MyBatis】文章目錄

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章