Mapper XML 文件

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。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末牲证,一起剝皮案震驚了整個濱河市哮针,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌坦袍,老刑警劉巖十厢,帶你破解...
    沈念sama閱讀 212,686評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異捂齐,居然都是意外死亡寿烟,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,668評論 3 385
  • 文/潘曉璐 我一進店門辛燥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來筛武,“玉大人,你說我怎么就攤上這事挎塌∨橇” “怎么了?”我有些...
    開封第一講書人閱讀 158,160評論 0 348
  • 文/不壞的土叔 我叫張陵榴都,是天一觀的道長待锈。 經常有香客問我,道長嘴高,這世上最難降的妖魔是什么竿音? 我笑而不...
    開封第一講書人閱讀 56,736評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮拴驮,結果婚禮上春瞬,老公的妹妹穿的比我還像新娘。我一直安慰自己套啤,他們只是感情好宽气,可當我...
    茶點故事閱讀 65,847評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著潜沦,像睡著了一般萄涯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上唆鸡,一...
    開封第一講書人閱讀 50,043評論 1 291
  • 那天涝影,我揣著相機與錄音,去河邊找鬼争占。 笑死燃逻,一個胖子當著我的面吹牛序目,可吹牛的內容都是我干的。 我是一名探鬼主播唆樊,決...
    沈念sama閱讀 39,129評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼刻蟹!你這毒婦竟也來了逗旁?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,872評論 0 268
  • 序言:老撾萬榮一對情侶失蹤舆瘪,失蹤者是張志新(化名)和其女友劉穎片效,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體英古,經...
    沈念sama閱讀 44,318評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡淀衣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,645評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了召调。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片膨桥。...
    茶點故事閱讀 38,777評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖唠叛,靈堂內的尸體忽然破棺而出只嚣,到底是詐尸還是另有隱情,我是刑警寧澤艺沼,帶...
    沈念sama閱讀 34,470評論 4 333
  • 正文 年R本政府宣布册舞,位于F島的核電站,受9級特大地震影響障般,放射性物質發(fā)生泄漏调鲸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,126評論 3 317
  • 文/蒙蒙 一挽荡、第九天 我趴在偏房一處隱蔽的房頂上張望藐石。 院中可真熱鬧,春花似錦定拟、人聲如沸贯钩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,861評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽角雷。三九已至,卻和暖如春性穿,著一層夾襖步出監(jiān)牢的瞬間勺三,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,095評論 1 267
  • 我被黑心中介騙來泰國打工需曾, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留吗坚,地道東北人祈远。 一個月前我還...
    沈念sama閱讀 46,589評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像商源,于是被迫代替她去往敵國和親车份。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,687評論 2 351

推薦閱讀更多精彩內容