【MyBatis系列3】收藏!最全MyBatis中XML映射文件(Mapper)標籤分析及示例 前言 Mapper文件標籤 總結

前言

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>

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

<select
id="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="">

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);

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

可選標籤

resultOrdered

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

resultSets

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

lang

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

insert

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

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

有一些標籤和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;
    }
}

定義一個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);
}

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

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

那麼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" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lonelyWolf.mybatis.mapper.UserAddressMapper">

   <insert id="insert" parameterType="com.lonelyWolf.mybatis.model.UserAddress" useGeneratedKeys="true" keyProperty="id">
       insert into lw_user_address (address) values (#{address})
   </insert>
</mapper>

mybatis-config.xml中要新增mapper映射文件配置:

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

然後寫一個測試類:

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();
        }
    }
}

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

插入成功數:1
插入數據的主鍵爲:1

通過selectKey獲取自定義列

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

int insert2(UserAddress userAddress);

然後在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>

然後修改測試類:

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();
        }

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

插入成功數:1
插入數據的address爲:097dfc8b-f043-11ea-97c4-00163e12524a

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

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

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

update

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

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

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

delete

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

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

這裏的標籤除了少了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>

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

 <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>

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

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

cache

MyBatis 包含一個非常強大的查詢緩存特性,它可以非常方便地配置和定製。但是默認情況下只開啓了一級緩存,即局部的session緩存,如果想要開啓二級緩存。那麼就需要使用到cache標籤

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

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

<cache/>

關於默認值是多少,請繼續往下看。。。。

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.UserAddressMapper"/>

這樣當前Mapper就可以共用UserMapper文件中的相同緩存了。

需要注意的是,被引用的namespace自己本身要開啓了二級緩存,否則會報錯:


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>

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

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

可以看到,resultMap可以自由定義,所以可以接受非常複雜的查詢返回結果集

parameterMap

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

總結

本文主要介紹了MyBatis中映射文件Mapper.xml文件一些標籤的使用,可以算是最全MyBatis中XML映射文件標籤分析了,當然這其中並不涉及到如何完成動態sql語句的拼寫。動態sql也是MyBatis的一個非常重要的功能點,但是綜合考慮如果單純通過一篇文章來書寫sql語句動態標籤的使用,會顯得非常枯燥,所以動態sql的映射就不準備通過單獨的文章來書寫了,雖然如此,但是依然會通過一些相關知識點來介紹動態標籤的使用,比如下一篇介紹MyBatis中一對多和多對多的結果集如何返回時,就會涉及到一些標籤,如where,if,set,choose等,後面還會介紹批量操作等,也會涉及到foreach等一些標籤的使用。

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