mybatis XML映射文件

原文地址:mybatis官方API:mybatis

1、XML 映射文件

1.1 XML映射器

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

  • cache – 該命名空間的緩存配置。
  • cache-ref – 引用其它命名空間的緩存配置。
  • resultMap – 描述如何從數據庫結果集中加載對象,是最複雜也是最強大的元素。
  • parameterMap – 老式風格的參數映射。此元素已被廢棄,並可能在將來被移除!請使用行內參數映射。文檔中不會介紹此元素。
  • sql – 可被其它語句引用的可重用語句塊。
  • insert – 映射插入語句。
  • update – 映射更新語句。
  • delete – 映射刪除語句。
  • select – 映射查詢語句。

1.2 select

MyBatis 的基本原則之一是:在每個插入、更新或刪除操作之間,通常會執行多個查詢操作。

<select id="selectPerson" parameterType="int" resultType="hashmap">
  SELECT * FROM PERSON WHERE ID = #{id}
</select>

接受一個 int(或 Integer)類型的參數,並返回一個 HashMap 類型的對象,其中的鍵是列名,值便是結果行中的對應值。

select 元素允許你配置很多屬性來配置每條語句的行爲細節。

<select
  id="selectPerson"
  parameterType="int"
  parameterMap="deprecated"
  resultType="hashmap"
  resultMap="personResultMap"
  flushCache="false"
  useCache="true"
  timeout="10"
  fetchSize="256"
  statementType="PREPARED"
  resultSetType="FORWARD_ONLY">

1.2.1 Select 元素的屬性

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

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

1.3.2 selectKey 元素描述如下:

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

1.4 sql

這個元素可以用來定義可重用的 SQL 代碼片段,以便在其它語句中使用。 參數可以靜態地(在加載的時候)確定下來,並且可以在不同的 include 元素中定義不同的參數值。比如:

<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 屬性或內部語句中使用屬性值,例如:

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

1.5 參數

<insert id="insertUser" parameterType="User">
  insert into users (id, username, password)
  values (#{id}, #{username}, #{password})
</insert>

如果 User 類型的參數對象傳遞到了語句中,會查找 id、username 和 password 屬性,然後將它們的值傳入預處理語句的參數中。

首先,和 MyBatis 的其它部分一樣,參數也可以指定一個特殊的數據類型。
#{property,javaType=int,jdbcType=NUMERIC}
MyBatis 的其它部分一樣,幾乎總是可以根據參數對象的類型確定 javaType,除非該對象是一個 HashMap。這個時候,你需要顯式指定 javaType 來確保正確的類型處理器(TypeHandler)被使用。

進一步地自定義類型處理方式,可以指定一個特殊的類型處理器類(或別名),比如:
#{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}

對於數值類型,還可以設置 numericScale 指定小數點後保留的位數。
#{height,javaType=double,jdbcType=NUMERIC,numericScale=2}

mode 屬性允許你指定 IN,OUT 或 INOUT 參數。如果參數的 mode 爲 OUT 或 INOUT,將會修改參數對象的屬性值,以便作爲輸出參數返回。 如果 mode 爲 OUT(或 INOUT),而且 jdbcType 爲 CURSOR(也就是 Oracle 的 REFCURSOR),你必須指定一個 resultMap 引用來將結果集 ResultMap 映射到參數的類型上。要注意這裏的 javaType 屬性是可選的,如果留空並且 jdbcType 是 CURSOR,它會被自動地被設爲 ResultMap。
#{department, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=departmentResultMap}

MyBatis 也支持很多高級的數據類型,比如結構體(structs),但是當使用 out 參數時,你必須顯式設置類型的名稱。
#{middleInitial, mode=OUT, jdbcType=STRUCT, jdbcTypeName=MY_TYPE, resultMap=departmentResultMap}

1.6 字符串替換

默認情況下,使用 #{} 參數語法時,MyBatis 會創建 PreparedStatement 參數佔位符,並通過佔位符安全地設置參數(就像使用 ? 一樣)。 這樣做更安全,更迅速,通常也是首選做法,不過有時你就是想直接在 SQL 語句中直接插入一個不轉義的字符串。 比如 ORDER BY 子句,這時候你可以:
ORDER BY ${columnName}

當 SQL 語句中的元數據(如表名或列名)是動態生成的時候,字符串替換將會非常有用。 舉個例子,如果你想 select 一個表任意一列的數據時,不需要這樣寫:

@Select("select * from user where id = #{id}")
User findById(@Param("id") long id);

@Select("select * from user where name = #{name}")
User findByName(@Param("name") String name);

@Select("select * from user where email = #{email}")
User findByEmail(@Param("email") String email);

// 其它的 "findByXxx" 方法

而是可以只寫這樣一個方法:

@Select("select * from user where ${column} = #{value}")
User findByColumn(@Param("column") String column, @Param("value") String value);

其中 ${column} 會被直接替換,而 #{value} 會使用 ? 預處理。

User userOfId1 = userMapper.findByColumn("id", 1L);
User userOfNameKid = userMapper.findByColumn("name", "kid");
User userOfEmail = userMapper.findByColumn("email", "[email protected]");

用這種方式接受用戶的輸入,並用作語句參數是不安全的,會導致潛在的 SQL 注入攻擊。因此,要麼不允許用戶輸入這些字段,要麼自行轉義並檢驗這些參數。

1.7 結果映射

ResultMap 的設計思想是,對簡單的語句做到零配置,對於複雜一點的語句,只需要描述語句之間的關係就行了。

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

上述語句只是簡單地將所有的列映射到 HashMap 的鍵上,這由 resultType 屬性指定。
程序更可能會使用 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;
  //省略Set get方法

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

1,8 高級結果映射

<!-- 非常複雜的結果映射 -->
<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>

1.8.1 結果映射(resultMap)

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

ResultMap 的屬性列表:

1.8.2 id & result

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

這些元素是結果映射的基礎。id 和 result 元素都將一個列的值映射到一個簡單數據類型(String, int, double, Date 等)的屬性或字段。

這兩者之間的唯一不同是,id 元素對應的屬性會被標記爲對象的標識符,在比較對象實例時使用。 這樣可以提高整體的性能,尤其是進行緩存和嵌套結果映射(也就是連接映射)的時候。

Id 和 Result 的屬性

1.9 支持的JDBC類型

MyBatis 通過內置的 jdbcType 枚舉類型支持下面的 JDBC 類型。

1.10 構造方法

通過修改對象屬性的方式,可以滿足大多數的數據傳輸對象(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 元素是一樣的。

1.11 關聯

<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 屬性上有所不同。

1.12 關聯的嵌套select查詢

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

1.13 關聯的嵌套結果映射

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

1.14 關聯的多結果集(ResultSet)

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