最全MyBatis中XML對映檔案(Mapper)標籤分析及示例

beluga發表於2020-09-16

前言

MyBatis的強大之處就在於它的對映器檔案,而這也正是MyBatis的魔力所在,對於任何MyBatis的使用者來說,MyBatis的對映檔案是必須要掌握的。

Mapper檔案標籤

Mapper中一個提供了9個頂層標籤,除了1個已經過期的我們不需要去了解,另外8個都是必須要掌握的,只要熟練掌握了標籤的使用,使用MyBatis才能如魚得水。接下來我們就一個個來分析一下這些標籤的使用。

select

select用來對映查詢語句,是我們使用最多的一種標籤,也是最複雜的一種標籤。比如下面就是一個簡單的select標籤的使用:

<select id="listUserByUserName" parameterType="String" resultType="lwUser">
        select user_id,user_name from lw_user where user_name=#{userName}</select>123

select標籤內,提供了一些二級標籤,下面就列舉出了全部的二級標籤:

<selectid="selectPerson"parameterType="int"parameterMap="deprecated"resultType="hashmap"resultMap="personResultMap"flushCache="false"useCache="true"timeout="10000"fetchSize="256"statementType="PREPARED"resultSetType="FORWARD_ONLY"databaseId="mysql"resultOrdered="false"resultSets="xxx,xxx"lang="">12345678910111213141516

id

必選標籤。同一個名稱空間裡面的唯一識別符號,如果需要被外部介面呼叫,則需要和Mapper介面中的方法名保持一致。

parameterType

可選標籤。引數類的 完全限定名或別名,上面示例中的表示我們傳入的引數是一個String型別(關於別名如果不清楚的可以點選這裡)。如果不寫這個屬性的話,MyBatis在解析xml檔案的時候會預設設為unset,然後根據TypeHandler推斷出引數型別。如果有多個引數的情況下建議還是不寫這個引數,否則可能會出現引數型別轉換錯誤

parameterMap

這是一個過期的屬性,我們不做討論。

resultType

非必選標籤注意這裡的非選是因為resultType和resultMap不能並存,兩者能且只能選擇一個。主要是用來定義一個返回結果集物件的全限定名或者別名。如果接收引數是一個集合,那麼這裡定義的就是集合中可以包含的型別,而並不是集合本身。
比如示例中的寫法,那麼對應Mapper介面中,適用於以下兩種語句:

LwUser listUserByUserName(@Param("userName") String userName);List<LwUser> listUserByUserName(@Param("userName") String userName);12

resultMap

非必選標籤注意這裡的非選是因為resultType和resultMap不能並存,兩者能且只能選擇一個。resultMap型別的結果集對映,是MyBatis最強大的特性,在這裡我們不展開,過兩天會有一篇單獨介紹MyBatis一對一和一對多等複雜查詢時候會單獨介紹該屬性。 感興趣的可以先關注我,留意後面的文章。

flushCache

可選標籤。設定為 true時,任何時候只要語句被呼叫,都會導致本地快取和二級快取都會被清空,預設值:false。

useCache

可選標籤。設定為 true時將會導致本條語句的結果被二級快取,對 select 標籤語句預設值為true,對insert,delete,update等語句預設是false。

timeout

可選標籤。這個設定是在丟擲異常之前,驅動程式等待資料庫返回請求結果的秒數。預設值為 unset(依賴驅動)。

fetchSize

可選標籤。這是嘗試影響驅動程式每次批次返回的結果行數和這個設定值相等。預設值為 unset(依賴驅動)。注意這個只是嘗試,假如把fetchSize設定為10萬,而資料庫驅動最高只支援到5w,那麼也會只能返回5w資料

statementType

可選標籤。可以選擇:STATEMENT,PREPARED 或 CALLABLE 中的一個,這會讓 MyBatis 分別使用Statement,PreparedStatement 或 CallableStatement,預設值是PREPARED,也就是使用預編譯PreparedStatement 語句。

resultSetType

可選標籤。可以選擇以下三種型別中的一個,預設為unset(依賴驅動)。

  • FORWARD_ONLY:只允許遊標向前訪問
  • SCROLL_SENSITIVE:允許遊標雙向滾動,但不會及時更新資料,也就是說如果資料庫中的資料被修改過,並不會在resultSet中體現出來
  • SCROLL_INSENSITIVE:許遊標雙向滾動,如果資料庫中的資料被修改過,會及時更新到resultSet

上面的解釋可能有些人還是看不明白,我們先來看一段JDBC讀取結果集的操作:
在這裡插入圖片描述
而MyBatis只是把這些操作封裝了,底層實際上還是這個操作,rs.next()遊標向前滾,其實還有一個rs.previous()表示遊標可以向後滾。
所以FORWARD_ONLY只允許向前滾,訪問過的資料就會釋放記憶體,在某些場景中可以提升效能。
後面那兩個都是允許雙向滾動,所以即使訪問過得資料,記憶體也不能釋放。這兩個的區別就是一個對資料敏感,一個對資料不敏感。

  • 對資料不敏感 就是說當我們查詢出結果之後,會將整個結果集都快取在記憶體中,假如有一條資料還沒讀取到(還在while迴圈中)這時候有另外一個執行緒修改了這條資料,那麼當我們後面讀取這條資料的時候,還是讀取到修改之前的。
  • 對資料敏感 就是說當我們查詢出結果之後,只會快取一個rowid,而並不會快取整條資料,假如有一條資料還沒讀取到(還在while迴圈中)這時候有另外一個執行緒修改了這條資料,那麼當我們後面讀取這條資料的時候,會根據rowid去查詢資料,查詢到的就是最新的資料。不過需要注意的是,因為delete的時候資料其實還在,只是打了個標記,所以如果一條資料被刪除了,是體現不出來的。同理,insert也不影響,因為查詢出來的資料不包含insert資料的rowid。

如果對於MySQL的 InnoDB引擎的MVCC機制,那麼資料肯定是不會敏感的,因為其他事務改了當前事務也看不到。

databaseId

可選標籤。資料庫廠商id,詳細瞭解,可以 點選這裡

resultOrdered

可選標籤。這個設定僅針對巢狀結果 select 語句適用。如果為 true,就是假設包含了巢狀結果集或是分組了,這樣的話當返回一個主結果行的時候,就不會發生有對前面結果集的引用的情況。這就使得在獲取巢狀的結果集的時候不至於導致記憶體不夠用。預設值: false 。

resultSets

可選標籤。這個設定僅對多結果集的情況適用,它將列出語句執行後返回的結果集並每個結果集給一個名稱,名稱是逗號分隔的。

lang

自定義語言,這個我也沒用過,所以就不介紹了

insert

insert用來對映插入語句。以下就是一個insert標籤的全部二級標籤:

<insertid="insertLwUser"parameterType="lwUser"parameterMap="deprecated"flushCache="true"statementType="PREPARED"keyProperty=""keyColumn=""useGeneratedKeys=""timeout="20"databaseId="mysql"lang="">123456789101112

有一些標籤和select語句是重複的就不在重複介紹,主要來關注一下其他標籤。

useGeneratedKeys

可選標籤。配置為true時,MyBatis會使用JDBC的getGeneratedKeys方法來取出由資料庫內部生成的主鍵(比如:像 MySQL 和 SQL Server 這樣的關聯式資料庫管理系統的自動遞增欄位),預設值為false。

keyProperty

可選標籤。唯一標記一個屬性,MyBatis會將透過getGeneratedKeys 的返回值或者透過insert 語句的selectKey 子元素設定它的鍵值,預設值是unset 。如果希望得到多個生成的列,也可以是逗號分隔的屬性名稱列表

keyColumn

透過生成的鍵值設定表中的列名,這個設定僅在某些資料庫(像PostgreSQL)是必須的,當主鍵列不是表中的第一列的時候需要設定。如果希望得到多個生成的列,也可以是
逗號分隔的屬性名稱列表

獲取自增主鍵

獲取自增主鍵,可以透過keyProperty來對映
定義一個實體類:

package com.lonelyWolf.mybatis.model;public class UserAddress {
    private int id;
    private String address;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }}12345678910111213141516171819202122

定義一個UserAddressMapper.java介面:

package com.lonelyWolf.mybatis.mapper;import com.lonelyWolf.mybatis.model.UserAddress;import org.apache.ibatis.annotations.Param;public interface UserAddressMapper {
    int insert(UserAddress userAddress);}12345678

注意:這裡引數如果直接只有一個的話可以不適用@Param註解,這樣在xml檔案中可以直接使用JavaBean內的屬性名。如果使用了@Param註解,如下:

int insert(@Param("userAddress") UserAddress userAddress);1

那麼xml檔案中就可以使用#{userAddress.屬性名}來獲取屬性JavaBean內的屬性

定義一個UserAddressMapper.xml對映檔案( keyProperty="id"表示把主鍵的值設定到引數UserAddress類中的屬性id):

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "
   <insert id="insert" parameterType="com.lonelyWolf.mybatis.model.UserAddress" useGeneratedKeys="true" keyProperty="id">
       insert into lw_user_address (address) values (#{address})
   </insert></mapper>12345678

mybatis-config.xml中要新增mapper對映檔案配置:

<mappers>
        <mapper resource="com/lonelyWolf/mybatis/mapping/UserAddressMapper.xml"/>
    </mappers>123

然後寫一個測試類:

package com.lonelyWolf.mybatis;import com.lonelyWolf.mybatis.mapper.UserAddressMapper;import com.lonelyWolf.mybatis.model.UserAddress;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;import java.io.InputStream;public class TestMyBatisInsert {
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        //讀取mybatis-config配置檔案
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //建立SqlSessionFactory物件
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //建立SqlSession物件
        SqlSession session = sqlSessionFactory.openSession();
        try {
            UserAddress userAddress = new UserAddress();
            userAddress.setAddress("廣東深圳");
            UserAddressMapper userAddressMapper = session.getMapper(UserAddressMapper.class);
            int i = userAddressMapper.insert(userAddress);
            session.commit();
            System.out.println("插入成功數:" + i);
            System.out.println("插入資料的主鍵為:" + userAddress.getId());
        }finally {
            session.close();
        }
    }}1234567891011121314151617181920212223242526272829303132333435

輸出結果(成功獲取到了插入資料的主鍵):

插入成功數:1插入資料的主鍵為:112

透過selectKey獲取自定義列

假如有些資料庫不支援自增主鍵,或者說我們想插入自定義的主鍵,而又不想在業務程式碼中編寫邏輯,那麼就可以透過MyBatis的selectKey來獲取。
UserAddressMapper.java中新建一個方法:

int insert2(UserAddress userAddress);1

然後在UserAddressMapper.xml中對應新增一個insert2語句:

<insert id="insert2"  useGeneratedKeys="true" keyProperty="address">
    <selectKey keyProperty="address" resultType="String" order="BEFORE">
        select uuid() from lw_user_address    </selectKey>
        insert into lw_user_address (address) values (#{address})    </insert>123456

然後修改測試類:

try {
            UserAddress userAddress = new UserAddress();
            UserAddressMapper userAddressMapper = session.getMapper(UserAddressMapper.class);
            int i = userAddressMapper.insert2(userAddress);
            session.commit();
            System.out.println("插入成功數:" + i);
            System.out.println("插入資料的address為:" + userAddress.getAddress());
        }finally {
            session.close();
        }12345678910

輸出結果如下,成功獲得了我們自定義的address:

插入成功數:1插入資料的address為:097dfc8b-f043-11ea-97c4-00163e12524a12

selectKey中的order屬性有2個選擇:BEFORE和AFTER。

  • BEFORE:表示先執行selectKey的語句,然後將查詢到的值設定到JavaBean對應屬性上,然後再執行insert語句。
  • AFTER:表示先執行AFTER語句,然後再執行selectKey語句,並將selectKey得到的值設定到JavaBean中的屬性。上面示例中如果改成AFTER,那麼插入的address就會是空值,但是返回的JavaBean屬性內會有值。

PS: selectKey中返回的值只能有一條資料,如果滿足條件的資料有多條會報錯,所以一般都是用於生成主鍵,確保唯一,或者在selectKey後面的語句加上條件,確保唯一

update

insert用來對映更新語句。以下就是一個undate標籤的全部二級標籤:

<updateid="UpdateLwUser"parameterType="lwUser"parameterMap="deprecated"flushCache="true"statementType="PREPARED"keyProperty=""keyColumn=""useGeneratedKeys=""timeout="20"databaseId="mysql"lang="">123456789101112

這個標籤和insert基本一致,就不重複解釋了。

delete

delete用來對映刪除語句。以下就是一個delete標籤的全部二級標籤:

<deleteid="insertLwUser"parameterType="lwUser"parameterMap="deprecated"flushCache="true"statementType="PREPARED"timeout="20"databaseId="mysql"lang="">123456789

這裡的標籤除了少了useGeneratedKeys,keyProperty和keyColumn三個標籤之外,其餘的和insert,update一樣。

sql

這個元素可以被用來定義可重用的 SQL 程式碼段,可以包含在其他語句中。
如下是一個最簡單的例子:

  <select id="selectUserAddress" resultType="com.lonelyWolf.mybatis.model.UserAddress">
        select <include refid="myCloumn"></include> from lw_user_address    </select>
    <sql id="myCloumn" >
        id,address    </sql>1234567

如果說我們現在需要定義一個關聯語句,列來自於兩張不同的表,又該如何實現呢?這時候就可以透過制定引數的方式來實現了:

 <select id="selectUserAddress" resultType="com.lonelyWolf.mybatis.model.UserAddress">
        select        <include refid="myCloumn1">
            <property name="prefix1" value="u"/>
            <property name="prefix2" value="j"/>
        </include>
        from lw_user u inner join  lw_user_job j on u.user_id=j.user_id    </select>
    <sql id="myCloumn1" >
        ${prefix1}.user_id,${prefix2}.id    </sql>123456789101112

這時候列印出來的sql語句如下:

select u.user_id,j.id from lw_user u inner join lw_user_job j on u.user_id=j.user_id1

cache

MyBatis 包含一個非常強大的查詢快取特性,它可以非常方便地配置和定製。但是預設情況下只開啟了一級快取,即區域性的session快取,如果想要開啟二級快取。那麼就需要使用到cache標籤

<cachetype="com.lonelyWolf.xxx"eviction="FIFO"flushInterval="60000"readOnly="true"size="512"/>123456

PS:這些屬性都是有預設值的,所以一般情況下可以直接使用:

</cache>1

關於預設值是多少,請繼續往下看。。。。

type

如果說我們自己自定義了快取,那麼這裡可以配置自定義快取類的全限定名或者別名,如果沒有自定義快取,則不需要配置type屬性。

關於快取相關原理以及如何自定義快取,後面會有一篇文章專門介紹快取,本文主要還是介紹一下標籤的使用,不會過多涉及底層原理性問題。

eviction

快取回收策略,MyBatis中more提供了以下策略可以選擇:

  • LRU:最近最少使用演算法( 預設演算法)。移除最長時間不被使用的物件
  • FIFO:先進先出演算法。按物件進入快取的順序來移除它們
  • SOFT: 軟引用。移除基於垃圾回收器狀態和軟引用規則的物件。
  • WEAK: 弱引用。更積極地移除基於垃圾收集器狀態和弱引用規則的物件

flushInterval

重新整理間隔時間(單位是毫秒)。可以被設定為任意的正整數。 預設情況是不設定,也就是不會主動重新整理快取(只有等待sql被執行的時候才會被重新整理)

readOnly

是否只讀。屬性可以被設定為 true 或 false。如果設定為true,那麼只讀的快取會給所有呼叫者返回快取物件的相同示例,因為快取無法被修改。這在一定程度上可以提升效能。
預設是false,也就是可以修改快取,那麼當讀取快取的時候會透過序列化的方式返回快取物件的複製,雖然這麼做會慢一點,但是安全,因此預設才會設定為false,允許修改快取。

size

引用數目。通俗點就是可以快取的個數, 預設值是1024。超過了設定值的時候,就會採用上面的演算法進行覆蓋

cache-ref

假如我們在其中一個Mapper中已經配置好了快取,然後在其他Mapper想要共用,那麼在這樣的情況下就可以使用cache-ref元素來引用另外一個快取,從而不需要重複配置。如:

<cache-ref namespace="com.lonelyWolf.mybatis.mapper.UserMapper"/>1

這樣當前Mapper就可以共用UserMapper檔案中的相同快取了。

resultMap

上面介紹select標籤的時候提到,select標籤的返回結果可以使用resultMap,但是一旦我們使用了resultMap時,我們就必須要自己定義一個resultMap。
如下,我們定義了一個resultMap:

 <resultMap id="JobResultMap" type="lwUser">
        <result column="user_id" property="userId" jdbcType="VARCHAR" />
        <result column="user_name" property="userName" jdbcType="VARCHAR" />
    </resultMap>1234

這時候select語句就可以引用:

<select id="selectUserAndJob" resultMap="JobResultMap">
        select * from lw_user    </select>123

可以看到,resultMap可以自由定義,所以可以接受非常複雜的查詢返回結果集,resultMap的具體使用,下一篇會專門介紹,本文不過多詳細解釋

parameterMap

這個引數已過期,不再討論,可以忘掉有這個引數


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69981976/viewspace-2721734/,如需轉載,請註明出處,否則將追究法律責任。

相關文章