天天看點

MyBatis官方文檔-XML 映射檔案

最近更新: 15 七月 2019|版本: 3.5.2

文章目錄

    • XML 映射檔案
      • insert, update 和 delete
      • sql
      • 結果映射
        • 進階結果映射
        • 結果映射(resultMap)
        • id & result
        • 支援的 JDBC 類型
        • 構造方法
        • 關聯
        • 關聯的嵌套 Select 查詢
        • 關聯的嵌套結果映射
        • 關聯的多結果集(ResultSet)
        • 集合
        • 集合的嵌套 Select 查詢
        • 集合的嵌套結果映射
        • 集合的多結果集(ResultSet)
        • 鑒别器
      • 緩存
        • 使用自定義緩存
        • cache-ref

XML 映射檔案

MyBatis 的真正強大在于它的映射語句,這是它的魔力所在。由于它的異常強大,映射器的 XML 檔案就顯得相對簡單。如果拿它跟具有相同功能的 JDBC 代碼進行對比,你會立即發現省掉了将近 95% 的代碼。MyBatis 為聚焦于 SQL 而建構,以盡可能地為你減少麻煩。

SQL 映射檔案隻有很少的幾個頂級元素(按照應被定義的順序列出):

  • cache

    – 對給定命名空間的緩存配置。
  • cache-ref

    – 對其他命名空間緩存配置的引用。
  • resultMap

    – 是最複雜也是最強大的元素,用來描述如何從資料庫結果集中來加載對象。
  • parameterMap

    – 已被廢棄!老式風格的參數映射。更好的辦法是使用内聯參數,此元素可能在将來被移除。文檔中不會介紹此元素。
  • sql

    – 可被其他語句引用的可重用語句塊。
  • insert

    – 映射插入語句
  • update

    – 映射更新語句
  • delete

    – 映射删除語句
  • select

    – 映射查詢語句

下一部分将從語句本身開始來描述每個元素的細節。

insert, update 和 delete

資料變更語句 insert,update 和 delete 的實作非常接近:

<insert id="insertAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" keyProperty="" keyColumn="" useGeneratedKeys="" timeout="20">

<update id="updateAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20">

<delete id="deleteAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20">           

複制

屬性 描述
id 命名空間中的唯一辨別符,可被用來代表這條語句。
parameterType 将要傳入語句的參數的完全限定類名或别名。這個屬性是可選的,因為 MyBatis 可以通過類型處理器推斷出具體傳入語句的參數,預設值為未設定(unset)。
parameterMap 這是引用外部 parameterMap 的已經被廢棄的方法。請使用内聯參數映射和 parameterType 屬性。
flushCache 将其設定為 true 後,隻要語句被調用,都會導緻本地緩存和二級緩存被清空,預設值:true(對于 insert、update 和 delete 語句)。
timeout 這個設定是在抛出異常之前,驅動程式等待資料庫傳回請求結果的秒數。預設值為未設定(unset)(依賴驅動)。
statementType STATEMENT,PREPARED 或 CALLABLE 的一個。這會讓 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,預設值:PREPARED。
useGeneratedKeys (僅對 insert 和 update 有用)這會令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法來取出由資料庫内部生成的主鍵(比如:像 MySQL 和 SQL Server 這樣的關系資料庫管理系統的自動遞增字段),預設值:false。
keyProperty (僅對 insert 和 update 有用)唯一标記一個屬性,MyBatis 會通過 getGeneratedKeys 的傳回值或者通過 insert 語句的 selectKey 子元素設定它的鍵值,預設值:未設定(unset)。如果希望得到多個生成的列,也可以是逗号分隔的屬性名稱清單。
keyColumn (僅對 insert 和 update 有用)通過生成的鍵值設定表中的列名,這個設定僅在某些資料庫(像 PostgreSQL)是必須的,當主鍵列不是表中的第一列的時候需要設定。如果希望使用多個生成的列,也可以設定為逗号分隔的屬性名稱清單。
databaseId 如果配置了資料庫廠商辨別(databaseIdProvider),MyBatis 會加載所有的不帶 databaseId 或比對目前 databaseId 的語句;如果帶或者不帶的語句都有,則不帶的會被忽略。

下面就是 insert,update 和 delete 語句的示例:

<insert id="insertAuthor">
  insert into Author (id,username,password,email,bio)
  values (#{id},#{username},#{password},#{email},#{bio})
</insert>

<update id="updateAuthor">
  update Author set
    username = #{username},
    password = #{password},
    email = #{email},
    bio = #{bio}
  where id = #{id}
</update>

<delete id="deleteAuthor">
  delete from Author where id = #{id}
</delete>           

複制

如前所述,插入語句的配置規則更加豐富,在插入語句裡面有一些額外的屬性和子元素用來處理主鍵的生成,而且有多種生成方式。

首先,如果你的資料庫支援自動生成主鍵的字段(比如 MySQL 和 SQL Server),那麼你可以設定 useGeneratedKeys=”true”,然後再把 keyProperty 設定到目标屬性上就 OK 了。例如,如果上面的 Author 表已經對 id 使用了自動生成的列類型,那麼語句可以修改為:

<insert id="insertAuthor" useGeneratedKeys="true" keyProperty="id">
  insert into Author (username,password,email,bio)
  values (#{username},#{password},#{email},#{bio})
</insert>           

複制

如果你的資料庫還支援多行插入, 你也可以傳入一個

Author

數組或集合,并傳回自動生成的主鍵。

<insert id="insertAuthor" useGeneratedKeys="true" keyProperty="id">
  insert into Author (username, password, email, bio) values
  <foreach item="item" collection="list" separator=",">
    (#{item.username}, #{item.password}, #{item.email}, #{item.bio})
  </foreach>
</insert>           

複制

對于不支援自動生成類型的資料庫或可能不支援自動生成主鍵的 JDBC 驅動,MyBatis 有另外一種方法來生成主鍵。

這裡有一個簡單(甚至很傻)的示例,它可以生成一個随機 ID(你最好不要這麼做,但這裡展示了 MyBatis 處理問題的靈活性及其所關心的廣度):

<insert id="insertAuthor">
  <selectKey keyProperty="id" resultType="int" order="BEFORE">
    select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
  </selectKey>
  insert into Author
    (id, username, password, email,bio, favourite_section)
  values
    (#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=VARCHAR})
</insert>           

複制

在上面的示例中,selectKey 元素中的語句将會首先運作,Author 的 id 會被設定,然後插入語句會被調用。這可以提供給你一個與資料庫中自動生成主鍵類似的行為,同時保持了 Java 代碼的簡潔。

selectKey 元素描述如下:

<selectKey keyProperty="id" resultType="int" order="BEFORE" statementType="PREPARED">           

複制

屬性 描述
keyProperty selectKey 語句結果應該被設定的目标屬性。如果希望得到多個生成的列,也可以是逗号分隔的屬性名稱清單。
keyColumn 比對屬性的傳回結果集中的列名稱。如果希望得到多個生成的列,也可以是逗号分隔的屬性名稱清單。
resultType 結果的類型。MyBatis 通常可以推斷出來,但是為了更加精确,寫上也不會有什麼問題。MyBatis 允許将任何簡單類型用作主鍵的類型,包括字元串。如果希望作用于多個生成的列,則可以使用一個包含期望屬性的 Object 或一個 Map。
order 這可以被設定為 BEFORE 或 AFTER。如果設定為 BEFORE,那麼它會首先生成主鍵,設定 keyProperty 然後執行插入語句。如果設定為 AFTER,那麼先執行插入語句,然後是 selectKey 中的語句 - 這和 Oracle 資料庫的行為相似,在插入語句内部可能有嵌入索引調用。
statementType 與前面相同,MyBatis 支援 STATEMENT,PREPARED 和 CALLABLE 語句的映射類型,分别代表 PreparedStatement 和 CallableStatement 類型。

sql

這個元素可以被用來定義可重用的 SQL 代碼段,這些 SQL 代碼可以被包含在其他語句中。它可以(在加載的時候)被靜态地設定參數。 在不同的包含語句中可以設定不同的值到參數占位符上。比如:

<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>           

複制

這個 SQL 片段可以被包含在其他語句中,例如:

<select id="selectUsers" resultType="map">
  select
    <include refid="userColumns"><property name="alias" value="t1"/></include>,
    <include refid="userColumns"><property name="alias" value="t2"/></include>
  from some_table t1
    cross join some_table t2
</select>           

複制

屬性值也可以被用在 include 元素的 refid 屬性裡或 include 元素的内部語句中,例如:

<sql id="sometable">
  ${prefix}Table
</sql>

<sql id="someinclude">
  from
    <include refid="${include_target}"/>
</sql>

<select id="select" resultType="map">
  select
    field1, field2, field3
  <include refid="someinclude">
    <property name="prefix" value="Some"/>
    <property name="include_target" value="sometable"/>
  </include>
</select>           

複制

結果映射

resultMap

元素是 MyBatis 中最重要最強大的元素。它可以讓你從 90% 的 JDBC

ResultSets

資料提取代碼中解放出來,并在一些情形下允許你進行一些 JDBC 不支援的操作。實際上,在為一些比如連接配接的複雜語句編寫映射代碼的時候,一份

resultMap

能夠代替實作同等功能的長達數千行的代碼。ResultMap 的設計思想是,對于簡單的語句根本不需要配置顯式的結果映射,而對于複雜一點的語句隻需要描述它們的關系就行了。

你已經見過簡單映射語句的示例了,但并沒有顯式指定

resultMap

。比如:

<select id="selectUsers" resultType="map">
  select id, username, hashedPassword
  from some_table
  where id = #{id}
</select>           

複制

上述語句隻是簡單地将所有的列映射到

HashMap

的鍵上,這由

resultType

屬性指定。雖然在大部分情況下都夠用,但是 HashMap 不是一個很好的領域模型。你的程式更可能會使用 JavaBean 或 POJO(Plain Old Java Objects,普通老式 Java 對象)作為領域模型。MyBatis 對兩者都提供了支援。看看下面這個 JavaBean:

package com.someapp.model;
public class User {
  private int id;
  private String username;
  private String hashedPassword;

  public int getId() {
    return id;
  }
  public void setId(int id) {
    this.id = id;
  }
  public String getUsername() {
    return username;
  }
  public void setUsername(String username) {
    this.username = username;
  }
  public String getHashedPassword() {
    return hashedPassword;
  }
  public void setHashedPassword(String hashedPassword) {
    this.hashedPassword = hashedPassword;
  }
}           

複制

基于 JavaBean 的規範,上面這個類有 3 個屬性:id,username 和 hashedPassword。這些屬性會對應到 select 語句中的列名。

這樣的一個 JavaBean 可以被映射到

ResultSet

,就像映射到

HashMap

一樣簡單。

<select id="selectUsers" resultType="com.someapp.model.User">
  select id, username, hashedPassword
  from some_table
  where id = #{id}
</select>           

複制

類型别名是你的好幫手。使用它們,你就可以不用輸入類的完全限定名稱了。比如:

<!-- mybatis-config.xml 中 -->
<typeAlias type="com.someapp.model.User" alias="User"/>

<!-- SQL 映射 XML 中 -->
<select id="selectUsers" resultType="User">
  select id, username, hashedPassword
  from some_table
  where id = #{id}
</select>           

複制

這些情況下,MyBatis 會在幕後自動建立一個

ResultMap

,再基于屬性名來映射列到 JavaBean 的屬性上。如果列名和屬性名沒有精确比對,可以在 SELECT 語句中對列使用别名(這是一個基本的 SQL 特性)來比對标簽。比如:

<select id="selectUsers" resultType="User">
  select
    user_id             as "id",
    user_name           as "userName",
    hashed_password     as "hashedPassword"
  from some_table
  where id = #{id}
</select>           

複制

ResultMap

最優秀的地方在于,雖然你已經對它相當了解了,但是根本就不需要顯式地用到他們。 上面這些簡單的示例根本不需要下面這些繁瑣的配置。 但出于示範的原因,讓我們來看看最後一個示例中,如果使用外部的

resultMap

會怎樣,這也是解決列名不比對的另外一種方式。

<resultMap id="userResultMap" type="User">
  <id property="id" column="user_id" />
  <result property="username" column="user_name"/>
  <result property="password" column="hashed_password"/>
</resultMap>           

複制

而在引用它的語句中使用

resultMap

屬性就行了(注意我們去掉了

resultType

屬性)。比如:

<select id="selectUsers" resultMap="userResultMap">
  select user_id, user_name, hashed_password
  from some_table
  where id = #{id}
</select>           

複制

如果世界總是這麼簡單就好了。

進階結果映射

MyBatis 建立時的一個思想是:資料庫不可能永遠是你所想或所需的那個樣子。 我們希望每個資料庫都具備良好的第三範式或 BCNF 範式,可惜它們不總都是這樣。 如果能有一種完美的資料庫映射模式,所有應用程式都可以使用它,那就太好了,但可惜也沒有。 而 ResultMap 就是 MyBatis 對這個問題的答案。

比如,我們如何映射下面這個語句?

<!-- 非常複雜的語句 -->
<select id="selectBlogDetails" resultMap="detailedBlogResultMap">
  select
       B.id as blog_id,
       B.title as blog_title,
       B.author_id as blog_author_id,
       A.id as author_id,
       A.username as author_username,
       A.password as author_password,
       A.email as author_email,
       A.bio as author_bio,
       A.favourite_section as author_favourite_section,
       P.id as post_id,
       P.blog_id as post_blog_id,
       P.author_id as post_author_id,
       P.created_on as post_created_on,
       P.section as post_section,
       P.subject as post_subject,
       P.draft as draft,
       P.body as post_body,
       C.id as comment_id,
       C.post_id as comment_post_id,
       C.name as comment_name,
       C.comment as comment_text,
       T.id as tag_id,
       T.name as tag_name
  from Blog B
       left outer join Author A on B.author_id = A.id
       left outer join Post P on B.id = P.blog_id
       left outer join Comment C on P.id = C.post_id
       left outer join Post_Tag PT on PT.post_id = P.id
       left outer join Tag T on PT.tag_id = T.id
  where B.id = #{id}
</select>           

複制

你可能想把它映射到一個智能的對象模型,這個對象表示了一篇部落格,它由某位作者所寫,有很多的博文,每篇博文有零或多條的評論和标簽。 我們來看看下面這個完整的例子,它是一個非常複雜的結果映射(假設作者,部落格,博文,評論和标簽都是類型别名)。 不用緊張,我們會一步一步來說明。雖然它看起來令人望而生畏,但其實非常簡單。

<!-- 非常複雜的結果映射 -->
<resultMap id="detailedBlogResultMap" type="Blog">
  <constructor>
    <idArg column="blog_id" javaType="int"/>
  </constructor>
  <result property="title" column="blog_title"/>
  <association property="author" javaType="Author">
    <id property="id" column="author_id"/>
    <result property="username" column="author_username"/>
    <result property="password" column="author_password"/>
    <result property="email" column="author_email"/>
    <result property="bio" column="author_bio"/>
    <result property="favouriteSection" column="author_favourite_section"/>
  </association>
  <collection property="posts" ofType="Post">
    <id property="id" column="post_id"/>
    <result property="subject" column="post_subject"/>
    <association property="author" javaType="Author"/>
    <collection property="comments" ofType="Comment">
      <id property="id" column="comment_id"/>
    </collection>
    <collection property="tags" ofType="Tag" >
      <id property="id" column="tag_id"/>
    </collection>
    <discriminator javaType="int" column="draft">
      <case value="1" resultType="DraftPost"/>
    </discriminator>
  </collection>
</resultMap>           

複制

resultMap

元素有很多子元素和一個值得深入探讨的結構。 下面是

resultMap

元素的概念視圖。

結果映射(resultMap)

  • constructor

    - 用于在執行個體化類時,注入結果到構造方法中
  • idArg

    - ID 參數;标記出作為 ID 的結果可以幫助提高整體性能
    • arg

      - 将被注入到構造方法的一個普通結果
  • id

    – 一個 ID 結果;标記出作為 ID 的結果可以幫助提高整體性能
  • result

    – 注入到字段或 JavaBean 屬性的普通結果
  • association

    – 一個複雜類型的關聯;許多結果将包裝成這種類型
  • 嵌套結果映射 – 關聯本身可以是一個

    resultMap

    元素,或者從别處引用一個
  • collection

    – 一個複雜類型的集合
  • 嵌套結果映射 – 集合本身可以是一個

    resultMap

    元素,或者從别處引用一個
  • discriminator

    – 使用結果值來決定使用哪個

    resultMap

  • case

    – 基于某些值的結果映射
    • 嵌套結果映射 –

      case

      本身可以是一個

      resultMap

      元素,是以可以具有相同的結構和元素,或者從别處引用一個
屬性 描述
id 目前命名空間中的一個唯一辨別,用于辨別一個結果映射。
type 類的完全限定名, 或者一個類型别名(關于内置的類型别名,可以參考上面的表格)。
autoMapping 如果設定這個屬性,MyBatis将會為本結果映射開啟或者關閉自動映射。 這個屬性會覆寫全局的屬性 autoMappingBehavior。預設值:未設定(unset)。

最佳實踐 最好一步步地建立結果映射。單元測試可以在這個過程中起到很大幫助。 如果你嘗試一次建立一個像上面示例那樣的巨大的結果映射,那麼很可能會出現錯誤而且很難去使用它來完成工作。 從最簡單的形态開始,逐漸疊代。而且别忘了單元測試! 使用架構的缺點是有時候它們看上去像黑盒子(無論源代碼是否可見)。 為了確定你實作的行為和想要的一緻,最好的選擇是編寫單元測試。送出 bug 的時候它也能起到很大的作用。

下一部分将詳細說明每個元素。

id & result

<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>           

複制

這些是結果映射最基本的内容。id 和 result 元素都将一個列的值映射到一個簡單資料類型(String, int, double, Date 等)的屬性或字段。

這兩者之間的唯一不同是,id 元素表示的結果将是對象的辨別屬性,這會在比較對象執行個體時用到。 這樣可以提高整體的性能,尤其是進行緩存和嵌套結果映射(也就是連接配接映射)的時候。

兩個元素都有一些屬性:

屬性 描述
property 映射到列結果的字段或屬性。如果用來比對的 JavaBean 存在給定名字的屬性,那麼它将會被使用。否則 MyBatis 将會尋找給定名稱的字段。 無論是哪一種情形,你都可以使用通常的點式分隔形式進行複雜屬性導航。 比如,你可以這樣映射一些簡單的東西:“username”,或者映射到一些複雜的東西上:“address.street.number”。
column 資料庫中的列名,或者是列的别名。一般情況下,這和傳遞給 resultSet.getString(columnName) 方法的參數一樣。
javaType 一個 Java 類的完全限定名,或一個類型别名(關于内置的類型别名,可以參考上面的表格)。 如果你映射到一個 JavaBean,MyBatis 通常可以推斷類型。然而,如果你映射到的是 HashMap,那麼你應該明确地指定 javaType 來保證行為與期望的相一緻。
jdbcType JDBC 類型,所支援的 JDBC 類型參見這個表格之後的“支援的 JDBC 類型”。 隻需要在可能執行插入、更新和删除的且允許空值的列上指定 JDBC 類型。這是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 程式設計,你需要對可能存在空值的列指定這個類型。
typeHandler 我們在前面讨論過預設的類型處理器。使用這個屬性,你可以覆寫預設的類型處理器。 這個屬性值是一個類型處理器實作類的完全限定名,或者是類型别名。

支援的 JDBC 類型

為了以後可能的使用場景,MyBatis 通過内置的 jdbcType 枚舉類型支援下面的 JDBC 類型。

BIT FLOAT CHAR TIMESTAMP OTHER UNDEFINED
TINYINT REAL VARCHAR BINARY BLOB NVARCHAR
SMALLINT DOUBLE LONGVARCHAR VARBINARY CLOB NCHAR
INTEGER NUMERIC DATE LONGVARBINARY BOOLEAN NCLOB
BIGINT DECIMAL TIME NULL CURSOR ARRAY

構造方法

通過修改對象屬性的方式,可以滿足大多數的資料傳輸對象(Data Transfer Object, DTO)以及絕大部分領域模型的要求。但有些情況下你想使用不可變類。 一般來說,很少改變或基本不變的包含引用或資料的表,很适合使用不可變類。 構造方法注入允許你在初始化時為類設定屬性的值,而不用暴露出公有方法。MyBatis 也支援私有屬性和私有 JavaBean 屬性來完成注入,但有一些人更青睐于通過構造方法進行注入。 constructor 元素就是為此而生的。

看看下面這個構造方法:

public class User {
   //...
   public User(Integer id, String username, int age) {
     //...
  }
//...
}           

複制

為了将結果注入構造方法,MyBatis 需要通過某種方式定位相應的構造方法。 在下面的例子中,MyBatis 搜尋一個聲明了三個形參的的構造方法,參數類型以

java.lang.Integer

,

java.lang.String

int

的順序給出。

<constructor>
   <idArg column="id" javaType="int"/>
   <arg column="username" javaType="String"/>
   <arg column="age" javaType="_int"/>
</constructor>           

複制

當你在處理一個帶有多個形參的構造方法時,很容易搞亂 arg 元素的順序。 從版本 3.4.3 開始,可以在指定參數名稱的前提下,以任意順序編寫 arg 元素。 為了通過名稱來引用構造方法參數,你可以添加

@Param

注解,或者使用 ‘-parameters’ 編譯選項并啟用

useActualParamName

選項(預設開啟)來編譯項目。下面是一個等價的例子,盡管函數簽名中第二和第三個形參的順序與 constructor 元素中參數聲明的順序不比對。

<constructor>
   <idArg column="id" javaType="int" name="id" />
   <arg column="age" javaType="_int" name="age" />
   <arg column="username" javaType="String" name="username" />
</constructor>           

複制

如果存在名稱和類型相同的屬性,那麼可以省略

javaType

剩餘的屬性和規則和普通的 id 和 result 元素是一樣的。

屬性 描述
column 資料庫中的列名,或者是列的别名。一般情況下,這和傳遞給 resultSet.getString(columnName) 方法的參數一樣。
javaType 一個 Java 類的完全限定名,或一個類型别名(關于内置的類型别名,可以參考上面的表格)。 如果你映射到一個 JavaBean,MyBatis 通常可以推斷類型。然而,如果你映射到的是 HashMap,那麼你應該明确地指定 javaType 來保證行為與期望的相一緻。
jdbcType JDBC 類型,所支援的 JDBC 類型參見這個表格之前的“支援的 JDBC 類型”。 隻需要在可能執行插入、更新和删除的且允許空值的列上指定 JDBC 類型。這是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 程式設計,你需要對可能存在空值的列指定這個類型。
typeHandler 我們在前面讨論過預設的類型處理器。使用這個屬性,你可以覆寫預設的類型處理器。 這個屬性值是一個類型處理器實作類的完全限定名,或者是類型别名。
select 用于加載複雜類型屬性的映射語句的 ID,它會從 column 屬性中指定的列檢索資料,作為參數傳遞給此 select 語句。具體請參考關聯元素。
resultMap 結果映射的 ID,可以将嵌套的結果集映射到一個合适的對象樹中。 它可以作為使用額外 select 語句的替代方案。它可以将多表連接配接操作的結果映射成一個單一的 ResultSet。這樣的 ResultSet 将會将包含重複或部分資料重複的結果集。為了将結果集正确地映射到嵌套的對象樹中,MyBatis 允許你 “串聯”結果映射,以便解決嵌套結果集的問題。想了解更多内容,請參考下面的關聯元素。
name 構造方法形參的名字。從 3.4.3 版本開始,通過指定具體的參數名,你可以以任意順序寫入 arg 元素。參看上面的解釋。

關聯

<association property="author" column="blog_author_id" javaType="Author">
  <id property="id" column="author_id"/>
  <result property="username" column="author_username"/>
</association>           

複制

關聯(association)元素處理“有一個”類型的關系。 比如,在我們的示例中,一個部落格有一個使用者。關聯結果映射和其它類型的映射工作方式差不多。 你需要指定目标屬性名以及屬性的

javaType

(很多時候 MyBatis 可以自己推斷出來),在必要的情況下你還可以設定 JDBC 類型,如果你想覆寫擷取結果值的過程,還可以設定類型處理器。

關聯的不同之處是,你需要告訴 MyBatis 如何加載關聯。MyBatis 有兩種不同的方式加載關聯:

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

首先,先讓我們來看看這個元素的屬性。你将會發現,和普通的結果映射相比,它隻在 select 和 resultMap 屬性上有所不同。

屬性 描述
property 映射到列結果的字段或屬性。如果用來比對的 JavaBean 存在給定名字的屬性,那麼它将會被使用。否則 MyBatis 将會尋找給定名稱的字段。 無論是哪一種情形,你都可以使用通常的點式分隔形式進行複雜屬性導航。 比如,你可以這樣映射一些簡單的東西:“username”,或者映射到一些複雜的東西上:“address.street.number”。
javaType 一個 Java 類的完全限定名,或一個類型别名(關于内置的類型别名,可以參考上面的表格)。 如果你映射到一個 JavaBean,MyBatis 通常可以推斷類型。然而,如果你映射到的是 HashMap,那麼你應該明确地指定 javaType 來保證行為與期望的相一緻。
jdbcType JDBC 類型,所支援的 JDBC 類型參見這個表格之前的“支援的 JDBC 類型”。 隻需要在可能執行插入、更新和删除的且允許空值的列上指定 JDBC 類型。這是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 程式設計,你需要對可能存在空值的列指定這個類型。
typeHandler 我們在前面讨論過預設的類型處理器。使用這個屬性,你可以覆寫預設的類型處理器。 這個屬性值是一個類型處理器實作類的完全限定名,或者是類型别名。

關聯的嵌套 Select 查詢

屬性 描述
column 資料庫中的列名,或者是列的别名。一般情況下,這和傳遞給 resultSet.getString(columnName) 方法的參數一樣。 注意:在使用複合主鍵的時候,你可以使用 column="{prop1=col1,prop2=col2}" 這樣的文法來指定多個傳遞給嵌套 Select 查詢語句的列名。這會使得 prop1 和 prop2 作為參數對象,被設定為對應嵌套 Select 語句的參數。
select 用于加載複雜類型屬性的映射語句的 ID,它會從 column 屬性指定的列中檢索資料,作為參數傳遞給目标 select 語句。 具體請參考下面的例子。注意:在使用複合主鍵的時候,你可以使用 column="{prop1=col1,prop2=col2}" 這樣的文法來指定多個傳遞給嵌套 Select 查詢語句的列名。這會使得 prop1 和 prop2 作為參數對象,被設定為對應嵌套 Select 語句的參數。
fetchType 可選的。有效值為 lazy 和 eager。 指定屬性後,将在映射中忽略全局配置參數 lazyLoadingEnabled,使用屬性的值。

示例:

<resultMap id="blogResult" type="Blog">
  <association property="author" column="author_id" javaType="Author" select="selectAuthor"/>
</resultMap>

<select id="selectBlog" resultMap="blogResult">
  SELECT * FROM BLOG WHERE ID = #{id}
</select>

<select id="selectAuthor" resultType="Author">
  SELECT * FROM AUTHOR WHERE ID = #{id}
</select>           

複制

就是這麼簡單。我們有兩個 select 查詢語句:一個用來加載部落格(Blog),另外一個用來加載作者(Author),而且部落格的結果映射描述了應該使用

selectAuthor

語句加載它的 author 屬性。

其它所有的屬性将會被自動加載,隻要它們的列名和屬性名相比對。

這種方式雖然很簡單,但在大型資料集或大型資料表上表現不佳。這個問題被稱為“N+1 查詢問題”。 概括地講,N+1 查詢問題是這樣子的:

  • 你執行了一個單獨的 SQL 語句來擷取結果的一個清單(就是“+1”)。
  • 對清單傳回的每條記錄,你執行一個 select 查詢語句來為每條記錄加載詳細資訊(就是“N”)。

這個問題會導緻成百上千的 SQL 語句被執行。有時候,我們不希望産生這樣的後果。

好消息是,MyBatis 能夠對這樣的查詢進行延遲加載,是以可以将大量語句同時運作的開銷分散開來。 然而,如果你加載記錄清單之後立刻就周遊清單以擷取嵌套的資料,就會觸發所有的延遲加載查詢,性能可能會變得很糟糕。

是以還有另外一種方法。

關聯的嵌套結果映射

屬性 描述
resultMap 結果映射的 ID,可以将此關聯的嵌套結果集映射到一個合适的對象樹中。 它可以作為使用額外 select 語句的替代方案。它可以将多表連接配接操作的結果映射成一個單一的 ResultSet。這樣的 ResultSet 有部分資料是重複的。 為了将結果集正确地映射到嵌套的對象樹中, MyBatis 允許你“串聯”結果映射,以便解決嵌套結果集的問題。使用嵌套結果映射的一個例子在表格以後。
columnPrefix 當連接配接多個表時,你可能會不得不使用列别名來避免在 ResultSet 中産生重複的列名。指定 columnPrefix 列名字首允許你将帶有這些字首的列映射到一個外部的結果映射中。 詳細說明請參考後面的例子。
notNullColumn 預設情況下,在至少一個被映射到屬性的列不為空時,子對象才會被建立。 你可以在這個屬性上指定非空的列來改變預設行為,指定後,Mybatis 将隻在這些列非空時才建立一個子對象。可以使用逗号分隔來指定多個列。預設值:未設定(unset)。
autoMapping 如果設定這個屬性,MyBatis 将會為本結果映射開啟或者關閉自動映射。 這個屬性會覆寫全局的屬性 autoMappingBehavior。注意,本屬性對外部的結果映射無效,是以不能搭配 select 或 resultMap 元素使用。預設值:未設定(unset)。

之前,你已經看到了一個非常複雜的嵌套關聯的例子。 下面的例子則是一個非常簡單的例子,用于示範嵌套結果映射如何工作。 現在我們将部落格表和作者表連接配接在一起,而不是執行一個獨立的查詢語句,就像這樣:

<select id="selectBlog" resultMap="blogResult">
  select
    B.id            as blog_id,
    B.title         as blog_title,
    B.author_id     as blog_author_id,
    A.id            as author_id,
    A.username      as author_username,
    A.password      as author_password,
    A.email         as author_email,
    A.bio           as author_bio
  from Blog B left outer join Author A on B.author_id = A.id
  where B.id = #{id}
</select>           

複制

注意查詢中的連接配接,以及為確定結果能夠擁有唯一且清晰的名字,我們設定的别名。 這使得進行映射非常簡單。現在我們可以映射這個結果:

<resultMap id="blogResult" type="Blog">
  <id property="id" column="blog_id" />
  <result property="title" column="blog_title"/>
  <association property="author" column="blog_author_id" javaType="Author" resultMap="authorResult"/>
</resultMap>

<resultMap id="authorResult" type="Author">
  <id property="id" column="author_id"/>
  <result property="username" column="author_username"/>
  <result property="password" column="author_password"/>
  <result property="email" column="author_email"/>
  <result property="bio" column="author_bio"/>
</resultMap>           

複制

在上面的例子中,你可以看到,部落格(Blog)作者(author)的關聯元素委托名為 “authorResult” 的結果映射來加載作者對象的執行個體。

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

現在,上面的示例使用了外部的結果映射元素來映射關聯。這使得 Author 的結果映射可以被重用。 然而,如果你不打算重用它,或者你更喜歡将你所有的結果映射放在一個具有描述性的結果映射元素中。 你可以直接将結果映射作為子元素嵌套在内。這裡給出使用這種方式的等效例子:

<resultMap id="blogResult" type="Blog">
  <id property="id" column="blog_id" />
  <result property="title" column="blog_title"/>
  <association property="author" javaType="Author">
    <id property="id" column="author_id"/>
    <result property="username" column="author_username"/>
    <result property="password" column="author_password"/>
    <result property="email" column="author_email"/>
    <result property="bio" column="author_bio"/>
  </association>
</resultMap>           

複制

那如果部落格(blog)有一個共同作者(co-author)該怎麼辦?select 語句看起來會是這樣的:

<select id="selectBlog" resultMap="blogResult">
  select
    B.id            as blog_id,
    B.title         as blog_title,
    A.id            as author_id,
    A.username      as author_username,
    A.password      as author_password,
    A.email         as author_email,
    A.bio           as author_bio,
    CA.id           as co_author_id,
    CA.username     as co_author_username,
    CA.password     as co_author_password,
    CA.email        as co_author_email,
    CA.bio          as co_author_bio
  from Blog B
  left outer join Author A on B.author_id = A.id
  left outer join Author CA on B.co_author_id = CA.id
  where B.id = #{id}
</select>           

複制

回憶一下,Author 的結果映射定義如下:

<resultMap id="authorResult" type="Author">
  <id property="id" column="author_id"/>
  <result property="username" column="author_username"/>
  <result property="password" column="author_password"/>
  <result property="email" column="author_email"/>
  <result property="bio" column="author_bio"/>
</resultMap>           

複制

由于結果中的列名與結果映射中的列名不同。你需要指定

columnPrefix

以便重複使用該結果映射來映射 co-author 的結果。

<resultMap id="blogResult" type="Blog">
  <id property="id" column="blog_id" />
  <result property="title" column="blog_title"/>
  <association property="author" resultMap="authorResult" />
  <association property="coAuthor" resultMap="authorResult" columnPrefix="co_" />
</resultMap>           

複制

關聯的多結果集(ResultSet)

屬性 描述
column 當使用多個結果集時,該屬性指定結果集中用于與 foreignColumn 比對的列(多個列名以逗号隔開),以識别關系中的父類型與子類型。
foreignColumn 指定外鍵對應的列名,指定的列将與父類型中 column 的給出的列進行比對。
resultSet 指定用于加載複雜類型的結果集名字。

從版本 3.2.3 開始,MyBatis 提供了另一種解決 N+1 查詢問題的方法。

某些資料庫允許存儲過程傳回多個結果集,或一次性執行多個語句,每個語句傳回一個結果集。 我們可以利用這個特性,在不使用連接配接的情況下,隻通路資料庫一次就能獲得相關資料。

在例子中,存儲過程執行下面的查詢并傳回兩個結果集。第一個結果集會傳回部落格(Blog)的結果,第二個則傳回作者(Author)的結果。

SELECT * FROM BLOG WHERE ID = #{id}

SELECT * FROM AUTHOR WHERE ID = #{id}           

複制

在映射語句中,必須通過

resultSets

屬性為每個結果集指定一個名字,多個名字使用逗号隔開。

<select id="selectBlog" resultSets="blogs,authors" resultMap="blogResult" statementType="CALLABLE">
  {call getBlogsAndAuthors(#{id,jdbcType=INTEGER,mode=IN})}
</select>           

複制

現在我們可以指定使用 “authors” 結果集的資料來填充 “author” 關聯:

<resultMap id="blogResult" type="Blog">
  <id property="id" column="id" />
  <result property="title" column="title"/>
  <association property="author" javaType="Author" resultSet="authors" column="author_id" foreignColumn="id">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <result property="password" column="password"/>
    <result property="email" column="email"/>
    <result property="bio" column="bio"/>
  </association>
</resultMap>           

複制

你已經在上面看到了如何處理“有一個”類型的關聯。但是該怎麼處理“有很多個”類型的關聯呢?這就是我們接下來要介紹的。

集合

<collection property="posts" ofType="domain.blog.Post">
  <id property="id" column="post_id"/>
  <result property="subject" column="post_subject"/>
  <result property="body" column="post_body"/>
</collection>           

複制

集合元素和關聯元素幾乎是一樣的,它們相似的程度之高,以緻于沒有必要再介紹集合元素的相似部分。 是以讓我們來關注它們的不同之處吧。

我們來繼續上面的示例,一個部落格(Blog)隻有一個作者(Author)。但一個部落格有很多文章(Post)。 在部落格類中,這可以用下面的寫法來表示:

private List<Post> posts;           

複制

要像上面這樣,映射嵌套結果集合到一個 List 中,可以使用集合元素。 和關聯元素一樣,我們可以使用嵌套 Select 查詢,或基于連接配接的嵌套結果映射集合。

集合的嵌套 Select 查詢

首先,讓我們看看如何使用嵌套 Select 查詢來為部落格加載文章。

<resultMap id="blogResult" type="Blog">
  <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/>
</resultMap>

<select id="selectBlog" resultMap="blogResult">
  SELECT * FROM BLOG WHERE ID = #{id}
</select>

<select id="selectPostsForBlog" resultType="Post">
  SELECT * FROM POST WHERE BLOG_ID = #{id}
</select>           

複制

你可能會立刻注意到幾個不同,但大部分都和我們上面學習過的關聯元素非常相似。 首先,你會注意到我們使用的是集合元素。 接下來你會注意到有一個新的 “ofType” 屬性。這個屬性非常重要,它用來将 JavaBean(或字段)屬性的類型和集合存儲的類型區分開來。 是以你可以按照下面這樣來閱讀映射:

<collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/>           

複制

讀作: “posts 是一個存儲 Post 的 ArrayList 集合”

在一般情況下,MyBatis 可以推斷 javaType 屬性,是以并不需要填寫。是以很多時候你可以簡略成:

<collection property="posts" column="id" ofType="Post" select="selectPostsForBlog"/>           

複制

集合的嵌套結果映射

現在你可能已經猜到了集合的嵌套結果映射是怎樣工作的——除了新增的 “ofType” 屬性,它和關聯的完全相同。

首先, 讓我們看看對應的 SQL 語句:

<select id="selectBlog" resultMap="blogResult">
  select
  B.id as blog_id,
  B.title as blog_title,
  B.author_id as blog_author_id,
  P.id as post_id,
  P.subject as post_subject,
  P.body as post_body,
  from Blog B
  left outer join Post P on B.id = P.blog_id
  where B.id = #{id}
</select>           

複制

我們再次連接配接了部落格表和文章表,并且為每一列都賦予了一個有意義的别名,以便映射保持簡單。 要映射部落格裡面的文章集合,就這麼簡單:

<resultMap id="blogResult" type="Blog">
  <id property="id" column="blog_id" />
  <result property="title" column="blog_title"/>
  <collection property="posts" ofType="Post">
    <id property="id" column="post_id"/>
    <result property="subject" column="post_subject"/>
    <result property="body" column="post_body"/>
  </collection>
</resultMap>           

複制

再提醒一次,要記得上面 id 元素的重要性,如果你不記得了,請閱讀關聯部分的相關部分。

如果你喜歡更詳略的、可重用的結果映射,你可以使用下面的等價形式:

<resultMap id="blogResult" type="Blog">
  <id property="id" column="blog_id" />
  <result property="title" column="blog_title"/>
  <collection property="posts" ofType="Post" resultMap="blogPostResult" columnPrefix="post_"/>
</resultMap>

<resultMap id="blogPostResult" type="Post">
  <id property="id" column="id"/>
  <result property="subject" column="subject"/>
  <result property="body" column="body"/>
</resultMap>           

複制

集合的多結果集(ResultSet)

像關聯元素那樣,我們可以通過執行存儲過程實作,它會執行兩個查詢并傳回兩個結果集,一個是部落格的結果集,另一個是文章的結果集:

SELECT * FROM BLOG WHERE ID = #{id}

SELECT * FROM POST WHERE BLOG_ID = #{id}           

複制

在映射語句中,必須通過

resultSets

屬性為每個結果集指定一個名字,多個名字使用逗号隔開。

<select id="selectBlog" resultSets="blogs,posts" resultMap="blogResult">
  {call getBlogsAndPosts(#{id,jdbcType=INTEGER,mode=IN})}
</select>           

複制

我們指定 “posts” 集合将會使用存儲在 “posts” 結果集中的資料進行填充:

<resultMap id="blogResult" type="Blog">
  <id property="id" column="id" />
  <result property="title" column="title"/>
  <collection property="posts" ofType="Post" resultSet="posts" column="id" foreignColumn="blog_id">
    <id property="id" column="id"/>
    <result property="subject" column="subject"/>
    <result property="body" column="body"/>
  </collection>
</resultMap>           

複制

注意 對關聯或集合的映射,并沒有深度、廣度或組合上的要求。但在映射時要留意性能問題。 在探索最佳實踐的過程中,應用的單元測試和性能測試會是你的好幫手。 而 MyBatis 的好處在于,可以在不對你的代碼引入重大變更(如果有)的情況下,允許你之後改變你的想法。

進階關聯和集合映射是一個深度話題。文檔的介紹隻能到此為止。配合少許的實踐,你會很快了解全部的用法。

鑒别器

<discriminator javaType="int" column="draft">
  <case value="1" resultType="DraftPost"/>
</discriminator>           

複制

有時候,一個資料庫查詢可能會傳回多個不同的結果集(但總體上還是有一定的聯系的)。 鑒别器(discriminator)元素就是被設計來應對這種情況的,另外也能處理其它情況,例如類的繼承層次結構。 鑒别器的概念很好了解——它很像 Java 語言中的 switch 語句。

一個鑒别器的定義需要指定 column 和 javaType 屬性。column 指定了 MyBatis 查詢被比較值的地方。 而 javaType 用來確定使用正确的相等測試(雖然很多情況下字元串的相等測試都可以工作)。例如:

<resultMap id="vehicleResult" type="Vehicle">
  <id property="id" column="id" />
  <result property="vin" column="vin"/>
  <result property="year" column="year"/>
  <result property="make" column="make"/>
  <result property="model" column="model"/>
  <result property="color" column="color"/>
  <discriminator javaType="int" column="vehicle_type">
    <case value="1" resultMap="carResult"/>
    <case value="2" resultMap="truckResult"/>
    <case value="3" resultMap="vanResult"/>
    <case value="4" resultMap="suvResult"/>
  </discriminator>
</resultMap>           

複制

在這個示例中,MyBatis 會從結果集中得到每條記錄,然後比較它的 vehicle type 值。 如果它比對任意一個鑒别器的 case,就會使用這個 case 指定的結果映射。 這個過程是互斥的,也就是說,剩餘的結果映射将被忽略(除非它是擴充的,我們将在稍後讨論它)。 如果不能比對任何一個 case,MyBatis 就隻會使用鑒别器塊外定義的結果映射。 是以,如果 carResult 的聲明如下:

<resultMap id="carResult" type="Car">
  <result property="doorCount" column="door_count" />
</resultMap>           

複制

那麼隻有 doorCount 屬性會被加載。這是為了即使鑒别器的 case 之間都能分為完全獨立的一組,盡管和父結果映射可能沒有什麼關系。在上面的例子中,我們當然知道 cars 和 vehicles 之間有關系,也就是 Car 是一個 Vehicle。是以,我們希望剩餘的屬性也能被加載。而這隻需要一個小修改。

<resultMap id="carResult" type="Car" extends="vehicleResult">
  <result property="doorCount" column="door_count" />
</resultMap>           

複制

現在 vehicleResult 和 carResult 的屬性都會被加載了。

可能有人又會覺得映射的外部定義有點太冗長了。 是以,對于那些更喜歡簡潔的映射風格的人來說,還有另一種文法可以選擇。例如:

<resultMap id="vehicleResult" type="Vehicle">
  <id property="id" column="id" />
  <result property="vin" column="vin"/>
  <result property="year" column="year"/>
  <result property="make" column="make"/>
  <result property="model" column="model"/>
  <result property="color" column="color"/>
  <discriminator javaType="int" column="vehicle_type">
    <case value="1" resultType="carResult">
      <result property="doorCount" column="door_count" />
    </case>
    <case value="2" resultType="truckResult">
      <result property="boxSize" column="box_size" />
      <result property="extendedCab" column="extended_cab" />
    </case>
    <case value="3" resultType="vanResult">
      <result property="powerSlidingDoor" column="power_sliding_door" />
    </case>
    <case value="4" resultType="suvResult">
      <result property="allWheelDrive" column="all_wheel_drive" />
    </case>
  </discriminator>
</resultMap>           

複制

提示 請注意,這些都是結果映射,如果你完全不設定任何的 result 元素,MyBatis 将為你自動比對列和屬性。是以上面的例子大多都要比實際的更複雜。 這也表明,大多數資料庫的複雜度都比較高,我們不太可能一直依賴于這種機制。

緩存

MyBatis 内置了一個強大的事務性查詢緩存機制,它可以非常友善地配置和定制。 為了使它更加強大而且易于配置,我們對 MyBatis 3 中的緩存實作進行了許多改進。

預設情況下,隻啟用了本地的會話緩存,它僅僅對一個會話中的資料進行緩存。 要啟用全局的二級緩存,隻需要在你的 SQL 映射檔案中添加一行:

<cache/>           

複制

基本上就是這樣。這個簡單語句的效果如下:

  • 映射語句檔案中的所有 select 語句的結果将會被緩存。
  • 映射語句檔案中的所有 insert、update 和 delete 語句會重新整理緩存。
  • 緩存會使用最近最少使用算法(LRU, Least Recently Used)算法來清除不需要的緩存。
  • 緩存不會定時進行重新整理(也就是說,沒有重新整理間隔)。
  • 緩存會儲存清單或對象(無論查詢方法傳回哪種)的 1024 個引用。
  • 緩存會被視為讀/寫緩存,這意味着擷取到的對象并不是共享的,可以安全地被調用者修改,而不幹擾其他調用者或線程所做的潛在修改。

提示 緩存隻作用于 cache 标簽所在的映射檔案中的語句。如果你混合使用 Java API 和 XML 映射檔案,在共用接口中的語句将不會被預設緩存。你需要使用 @CacheNamespaceRef 注解指定緩存作用域。

這些屬性可以通過 cache 元素的屬性來修改。比如:

<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>           

複制

這個更進階的配置建立了一個 FIFO 緩存,每隔 60 秒重新整理,最多可以存儲結果對象或清單的 512 個引用,而且傳回的對象被認為是隻讀的,是以對它們進行修改可能會在不同線程中的調用者産生沖突。

可用的清除政策有:

  • LRU

    – 最近最少使用:移除最長時間不被使用的對象。
  • FIFO

    – 先進先出:按對象進入緩存的順序來移除它們。
  • SOFT

    – 軟引用:基于垃圾回收器狀态和軟引用規則移除對象。
  • WEAK

    – 弱引用:更積極地基于垃圾收集器狀态和弱引用規則移除對象。

預設的清除政策是 LRU。

flushInterval(重新整理間隔)屬性可以被設定為任意的正整數,設定的值應該是一個以毫秒為機關的合理時間量。 預設情況是不設定,也就是沒有重新整理間隔,緩存僅僅會在調用語句時重新整理。

size(引用數目)屬性可以被設定為任意正整數,要注意欲緩存對象的大小和運作環境中可用的記憶體資源。預設值是 1024。

readOnly(隻讀)屬性可以被設定為 true 或 false。隻讀的緩存會給所有調用者傳回緩存對象的相同執行個體。 是以這些對象不能被修改。這就提供了可觀的性能提升。而可讀寫的緩存會(通過序列化)傳回緩存對象的拷貝。 速度上會慢一些,但是更安全,是以預設值是 false。

提示 二級緩存是事務性的。這意味着,當 SqlSession 完成并送出時,或是完成并復原,但沒有執行 flushCache=true 的 insert/delete/update 語句時,緩存會獲得更新。

使用自定義緩存

除了上述自定義緩存的方式,你也可以通過實作你自己的緩存,或為其他第三方緩存方案建立擴充卡,來完全覆寫緩存行為。

<cache type="com.domain.something.MyCustomCache"/>           

複制

這個示例展示了如何使用一個自定義的緩存實作。type 屬性指定的類必須實作 org.mybatis.cache.Cache 接口,且提供一個接受 String 參數作為 id 的構造器。 這個接口是 MyBatis 架構中許多複雜的接口之一,但是行為卻非常簡單。

public interface Cache {
  String getId();
  int getSize();
  void putObject(Object key, Object value);
  Object getObject(Object key);
  boolean hasKey(Object key);
  Object removeObject(Object key);
  void clear();
}           

複制

為了對你的緩存進行配置,隻需要簡單地在你的緩存實作中添加公有的 JavaBean 屬性,然後通過 cache 元素傳遞屬性值,例如,下面的例子将在你的緩存實作上調用一個名為

setCacheFile(String file)

的方法:

<cache type="com.domain.something.MyCustomCache">
  <property name="cacheFile" value="/tmp/my-custom-cache.tmp"/>
</cache>           

複制

你可以使用所有簡單類型作為 JavaBean 屬性的類型,MyBatis 會進行轉換。 你也可以使用占位符(如

${cache.file}

),以便替換成在配置檔案屬性中定義的值。

從版本 3.4.2 開始,MyBatis 已經支援在所有屬性設定完畢之後,調用一個初始化方法。 如果想要使用這個特性,請在你的自定義緩存類裡實作

org.apache.ibatis.builder.InitializingObject

接口。

public interface InitializingObject {
  void initialize() throws Exception;
}           

複制

提示 上一節中對緩存的配置(如清除政策、可讀或可讀寫等),不能應用于自定義緩存。

請注意,緩存的配置和緩存執行個體會被綁定到 SQL 映射檔案的命名空間中。 是以,同一命名空間中的所有語句和緩存将通過命名空間綁定在一起。 每條語句可以自定義與緩存互動的方式,或将它們完全排除于緩存之外,這可以通過在每條語句上使用兩個簡單屬性來達成。 預設情況下,語句會這樣來配置:

<select ... flushCache="false" useCache="true"/>
<insert ... flushCache="true"/>
<update ... flushCache="true"/>
<delete ... flushCache="true"/>           

複制

鑒于這是預設行為,顯然你永遠不應該以這樣的方式顯式配置一條語句。但如果你想改變預設的行為,隻需要設定 flushCache 和 useCache 屬性。比如,某些情況下你可能希望特定 select 語句的結果排除于緩存之外,或希望一條 select 語句清空緩存。類似地,你可能希望某些 update 語句執行時不要重新整理緩存。

cache-ref

回想一下上一節的内容,對某一命名空間的語句,隻會使用該命名空間的緩存進行緩存或重新整理。 但你可能會想要在多個命名空間中共享相同的緩存配置和執行個體。要實作這種需求,你可以使用 cache-ref 元素來引用另一個緩存。

<cache-ref namespace="com.someone.application.data.SomeMapper"/>           

複制

我的部落格即将同步至騰訊雲+社群,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=1ocwqsxb7tvjr

本文由來源 ThinkWon的部落格,由 system_mush 整理編輯,其版權均為 ThinkWon的部落格 所有,文章内容系作者個人觀點,不代表 Java架構師必看 對觀點贊同或支援。如需轉載,請注明文章來源。