MyBatis 的真正強大在于它的映射語句汇恤,也是它的魔力所在毡泻。由于它的異常強大晕窑,映射器的 XML 文件就顯得相對簡單室叉。如果拿它跟具有相同功能的 JDBC 代碼進行對比睹栖,你會立即發(fā)現(xiàn)省掉了將近 95% 的代碼。MyBatis 就是針對 SQL 構建的茧痕,并且比普通的方法做的更好野来。
SQL 映射文件有很少的幾個頂級元素(按照它們應該被定義的順序):
-
cache
– 給定命名空間的緩存配置。 -
cache-ref
– 其他命名空間緩存配置的引用踪旷。 -
resultMap
– 是最復雜也是最強大的元素曼氛,用來描述如何從數(shù)據(jù)庫結果集中來加載對象。 -
parameterMap
–已廢棄
令野!老式風格的參數(shù)映射舀患。內聯(lián)參數(shù)是首選,這個元素可能在將來被移除,這里不會記錄气破。 -
sql
– 可被其他語句引用的可重用語句塊聊浅。 -
insert
– 映射插入語句 -
update
– 映射更新語句 -
delete
– 映射刪除語句 -
select
– 映射查詢語句
下一部分將從語句本身開始來描述每個元素的細節(jié)。
select
查詢語句是 MyBatis 中最常用的元素之一堵幽,光能把數(shù)據(jù)存到數(shù)據(jù)庫中價值并不大狗超,如果還能重新取出來才有用,多數(shù)應用也都是查詢比修改要頻繁朴下。對每個插入努咐、更新或刪除操作,通常對應多個查詢操作殴胧。這是 MyBatis 的基本原則之一渗稍,也是將焦點和努力放到查詢和結果映射的原因。簡單查詢的 select 元素是非常簡單的团滥。比如:
<select id="selectPerson" parameterType="int" resultType="hashmap">
SELECT * FROM PERSON WHERE ID = #{id}
</select>
這個語句被稱作 selectPerson竿屹,接受一個 int(或 Integer)類型的參數(shù),并返回一個 HashMap 類型的對象灸姊,其中的鍵是列名拱燃,值便是結果行中的對應值。
注意參數(shù)符號:
#{id}
這就告訴 MyBatis 創(chuàng)建一個預處理語句參數(shù)力惯,通過 JDBC碗誉,這樣的一個參數(shù)在 SQL 中會由一個“?”來標識,并被傳遞到一個新的預處理語句中父晶,就像這樣:
// Similar JDBC code, NOT MyBatis…
String selectPerson = "SELECT * FROM PERSON WHERE ID=?";
PreparedStatement ps = conn.prepareStatement(selectPerson);
ps.setInt(1,id);
當然哮缺,這需要很多單獨的 JDBC 的代碼來提取結果并將它們映射到對象實例中,這就是 MyBatis 節(jié)省你時間的地方甲喝。
insert, update 和 delete
下面就是 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>
如前所述,插入語句的配置規(guī)則更加豐富,在插入語句里面有一些額外的屬性和子元素用來處理主鍵的生成糠溜,而且有多種生成方式淳玩。
首先,如果你的數(shù)據(jù)庫支持自動生成主鍵的字段(比如 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>
sql
這個元素可以被用來定義可重用的 SQL 代碼段,可以包含在其他語句中豹芯。它可以被靜態(tài)地(在加載參數(shù)) 參數(shù)化. 不同的屬性值通過包含的實例變化. 比如:
<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>
參數(shù)(Parameters)
前面的所有語句中你所見到的都是簡單參數(shù)的例子,實際上參數(shù)是 MyBatis 非常強大的元素铁蹈,對于簡單的做法宽闲,大概 90% 的情況參數(shù)都很少,比如:
<select id="selectUsers" resultType="User">
select id, username, password
from users
where id = #{id}
</select>
Result Maps
resultMap
元素是 MyBatis 中最重要最強大的元素握牧。它可以讓你從 90% 的 JDBC ResultSets
數(shù)據(jù)提取代碼中解放出來, 并在一些情形下允許你做一些 JDBC 不支持的事情容诬。 實際上,在對復雜語句進行聯(lián)合映射的時候沿腰,它很可能可以代替數(shù)千行的同等功能的代碼览徒。 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 的規(guī)范,上面這個類有 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>
類型別名是你的好幫手。使用它們,你就可以不用輸入類的完全限定名稱了绍填。比如:
<!-- In mybatis-config.xml file -->
<typeAlias type="com.someapp.model.User" alias="User"/>
<!-- In SQL Mapping XML file -->
<select id="selectUsers" resultType="User">
select id, username, hashedPassword
from some_table
where id = #{id}
</select>
這些情況下霎桅,MyBatis 會在幕后自動創(chuàng)建一個 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
最優(yōu)秀的地方在于卿闹,雖然你已經對它相當了解了揭糕,但是根本就不需要顯式地用到他們。 上面這些簡單的示例根本不需要下面這些繁瑣的配置锻霎。 出于示范的原因著角,讓我們來看看最后一個示例中,如果使用外部的 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 創(chuàng)建的一個想法是:數(shù)據(jù)庫不可能永遠是你所想或所需的那個樣子冰更。 我們希望每個數(shù)據(jù)庫都具備良好的第三范式或 BCNF 范式产徊,可惜它們不總都是這樣。 如果有一個獨立且完美的數(shù)據(jù)庫映射模式蜀细,所有應用程序都可以使用它舟铜,那就太好了,但可惜也沒有奠衔。 ResultMap 就是 MyBatis 對這個問題的答案深滚。
比如,我們如何映射下面這個語句涣觉?
<!-- Very Complex Statement -->
<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 (假設作者,博客,博文,評論和標簽都是類型的別名)鸦难。 不用緊張,我們會一步一步來說明员淫。 雖然它看起來令人望而生畏合蔽,但其實非常簡單。
<!-- 超復雜的 Result Map -->
<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 參數(shù);標記出作為 ID 的結果可以幫助提高整體性能 -
arg
- 將被注入到構造方法的一個普通結果 -
id
– 一個 ID 結果;標記出作為 ID 的結果可以幫助提高整體性能 -
result
– 注入到字段或 JavaBean 屬性的普通結果 -
association
– 一個復雜類型的關聯(lián);許多結果將包裝成這種類型- 嵌套結果映射 – 關聯(lián)可以指定為一個 resultMap 元素,或者引用一個
-
collection
– 一個復雜類型的集合- 嵌套結果映射 – 集合可以指定為一個 resultMap 元素刃宵,或者引用一個
-
discriminator
– 使用結果值來決定使用哪個 resultMap-
case
– 基于某些值的結果映射- 嵌套結果映射 – 一個 case 也是一個映射它本身的結果,因此可以包含很多相 同的元素衡瓶,或者它可以參照一個外部的 resultMap。
-