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 也是一個結果映射,因此具有相同的結構和元素;或者引用其它的結果映射
- 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>