Mybatis基礎知識2-XML映射文件

3.XML映射文件

MyBatis 的真正強大在于它的映射語句,也是它的魔力所在娜膘。由于它的異常強大逊脯,映射器的 XML 文件就顯得相對簡單。如果拿它跟具有相同功能的 JDBC 代碼進行對比竣贪,你會立即發(fā)現(xiàn)省掉了將近 95% 的代碼军洼。MyBatis 就是針對 SQL 構建的,并且比普通的方法做的更好演怎。
SQL 映射文件有很少的幾個頂級元素(按照它們應該被定義的順序):

  • cache – 給定命名空間的緩存配置歉眷。
  • cache-ref – 其他命名空間緩存配置的引用。
  • resultMap – 是最復雜也是最強大的元素颤枪,用來描述如何從數(shù)據(jù)庫結果集中來加載對象汗捡。
  • sql – 可被其他語句引用的可重用語句塊。
  • insert – 映射插入語句
  • update – 映射更新語句
  • delete – 映射刪除語句
  • select – 映射查詢語句

3.1 select

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

類似的JDBC代碼:

    // Similar JDBC code, NOT MyBatis…
    String selectPerson = "SELECT * FROM PERSON WHERE ID=?";
    PreparedStatement ps = conn.prepareStatement(selectPerson);
    ps.setInt(1,id);

需要很多單獨的 JDBC 的代碼來提取結果并將它們映射到對象實例中,這就是 MyBatis 節(jié)省你時間的地方扇住。我們需要深入了解參數(shù)和結果映射春缕,細節(jié)部分我們下面來了解。
select 元素有很多屬性允許你配置艘蹋,來決定每條語句的作用細節(jié)锄贼。

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

3.2 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">
<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 表已經(jīng)對 id 使用了自動生成的列類型,那么語句可以修改為:

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

對于不支持自動生成類型的數(shù)據(jù)庫或可能不支持自動生成主鍵 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 會被設置兄裂,然后插入語句會被調(diào)用句旱。這給你了一個和數(shù)據(jù)庫中來處理自動生成的主鍵類似的行為,避免了使 Java 代碼變得復雜懦窘。

selectKey 元素描述如下:

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

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

3.4 參數(shù)

參數(shù)是 MyBatis 非常強大的元素港华,對于簡單的做法,大概 90% 的情況參數(shù)都很少午衰,比如:

<select id="selectUsers" resultType="User">
  select id, username, password
  from users
  where id = #{id}
</select>

上面的這個示例說明了一個非常簡單的命名參數(shù)映射立宜。參數(shù)類型被設置為 int,這樣這個參數(shù)就可以被設置成任何內(nèi)容臊岸。原生的類型或簡單數(shù)據(jù)類型(比如整型和字符串)因為沒有相關屬性橙数,它會完全用參數(shù)值來替代。然而帅戒,如果傳入一個復雜的對象灯帮,行為就會有一點不同了崖技。比如:

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

如果 User 類型的參數(shù)對象傳遞到了語句中,id钟哥、username 和 password 屬性將會被查找迎献,然后將它們的值傳入預處理語句的參數(shù)中。

這點對于向語句中傳參是比較好的而且又簡單腻贰,不過參數(shù)映射的功能遠不止于此吁恍。

首先,像 MyBatis 的其他部分一樣播演,參數(shù)也可以指定一個特殊的數(shù)據(jù)類型冀瓦。

#{property,javaType=int,jdbcType=NUMERIC}

像 MyBatis 的剩余部分一樣,javaType 通承纯荆可以從參數(shù)對象中來去確定翼闽,前提是只要對象不是一個 HashMap。那么 javaType 應該被確定來保證使用正確類型處理器顶霞。

NOTE: 如果 null 被當作值來傳遞肄程,對于所有可能為空的列锣吼,JDBC Type 是需要的选浑。你可以自己通過閱讀預處理語句的 setNull() 方法的 JavaDocs 文檔來研究這種情況。

為了以后定制類型處理方式玄叠,你也可以指定一個特殊的類型處理器類(或別名)古徒,比如:

#{age,javaType=int, jdbcType=NUMERIC, typeHandler=MyTypeHandler}

盡管看起來配置變得越來越繁瑣,但實際上是很少去設置它們读恃。

對于數(shù)值類型隧膘,還有一個小數(shù)保留位數(shù)的設置,來確定小數(shù)點后保留的位數(shù)寺惫。

#{height,javaType=double, jdbcType=NUMERIC, numericScale=2}

最后疹吃,mode 屬性允許你指定 IN,OUT 或 INOUT 參數(shù)西雀。如果參數(shù)為 OUT 或 INOUT萨驶,參數(shù)對象屬性的真實值將會被改變,就像你在獲取輸出參數(shù)時所期望的那樣艇肴。如果 mode 為 OUT(或 INOUT)腔呜,而且 jdbcType 為 CURSOR(也就是 Oracle 的 REFCURSOR),你必須指定一個 resultMap 來映射結果集到參數(shù)類型再悼。要注意這里的 javaType 屬性是可選的核畴,如果左邊的空白是 jdbcType 的 CURSOR 類型,它會自動地被設置為結果集冲九。

#{department, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=departmentResultMap}

MyBatis 也支持很多高級的數(shù)據(jù)類型谤草,比如結構體,但是當注冊 out 參數(shù)時你必須告訴它語句類型名稱。比如(再次提示丑孩,在實際中要像這樣不能換行):

#{middleInitial, mode=OUT, jdbcType=STRUCT, jdbcTypeName=MY_TYPE, resultMap=departmentResultMap}

盡管所有這些強大的選項很多時候你只簡單指定屬性名泳炉,其他的事情 MyBatis 會自己去推斷,最多你需要為可能為空的列名指定 jdbcType嚎杨。

#{firstName}
#{middleInitial,jdbcType=VARCHAR}
#{lastName}
  • 字符串替換
    默認情況下,使用#{}格式的語法會導致 MyBatis 創(chuàng)建預處理語句屬性并安全地設置值(比如?)花鹅。這樣做更安全,更迅速枫浙,通常也是首選做法刨肃,不過有時你只是想直接在 SQL 語句中插入一個不改變的字符串。比如箩帚,像 ORDER BY真友,你可以這樣來使用:

ORDER BY ${columnName}

這里 MyBatis 不會修改或轉義字符串。
NOTE: 以這種方式接受從用戶輸出的內(nèi)容并提供給語句中不變的字符串是不安全的紧帕,會導致潛在的 SQL 注入攻擊盔然,因此要么不允許用戶輸入這些字段,要么自行轉義并檢驗是嗜。

3.5 Result Maps

resultMap 元素是 MyBatis 中最重要最強大的元素愈案。它就是讓你遠離 90%的需要從結果 集中取出數(shù)據(jù)的 JDBC 代碼的那個東西, 而且在一些情形下允許你做一些 JDBC 不支持的事 情。 事實上, 編寫相似于對復雜語句聯(lián)合映射這些等同的代碼, 也許可以跨過上千行的代碼鹅搪。 ResultMap 的設計就是簡單語句不需要明確的結果映射,而很多復雜語句確實需要描述它們 的關系站绪。
使用 JavaBeans 或 POJOs(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 可以被映射到結果集,就像映射到 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)秀的地方你已經(jīng)了解了很多了,但是你還沒有真正的看到一個麻顶。這些簡 單的示例不需要比你看到的更多東西赦抖。 只是出于示例的原因, 讓我們來看看最后一個示例中 外部的 resultMap 是什么樣子的,這也是解決列名不匹配的另外一種方式。

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

引用它的語句使用 resultMap 屬性就行了(注意我們?nèi)サ袅?resultType 屬性)辅肾。比如:

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

3.5.1 ResultMap的高級功能

  • constructor - 類在實例化時,用來注入結果到構造方法中
    idArg - ID 參數(shù);標記結果作為 ID 可以幫助提高整體效能
    arg - 注入到構造方法的一個普通結果
  • id – 一個 ID 結果;標記結果作為 ID 可以幫助提高整體效能
  • result – 注入到字段或 JavaBean 屬性的普通結果
  • constructor
  • association – 一個復雜的類型關聯(lián);許多結果將包成這種類型
    嵌入結果映射 – 結果映射自身的關聯(lián),或者參考一個
  • collection – 復雜類型的集
    嵌入結果映射 – 結果映射自身的集,或者參考一個
  • discriminator – 使用結果值來決定使用哪個結果映射
    case – 基于某些值的結果映射
    嵌入結果映射 – 這種情形結果也映射它本身,因此可以包含很多相 同的元素,或者它可以參照一個外部的結果映射
  • 自動映射
    正如你在前面一節(jié)看到的队萤,在簡單的場景下,MyBatis可以替你自動映射查詢結果矫钓。 如果遇到復雜的場景要尔,你需要構建一個result map舍杜。 但是在本節(jié)你將看到,你也可以混合使用這兩種策略赵辕。 讓我們到深一點的層面上看看自動映射是怎樣工作的既绩。
    當自動映射查詢結果時,MyBatis會獲取sql返回的列名并在java類中查找相同名字的屬性(忽略大小寫)还惠。 這意味著如果Mybatis發(fā)現(xiàn)了_ID_列和_id_屬性饲握,Mybatis會將_ID_的值賦給id。
    通常數(shù)據(jù)庫列使用大寫單詞命名蚕键,單詞間用下劃線分隔救欧;而java屬性一般遵循駝峰命名法。 為了在這兩種命名方式之間啟用自動映射锣光,需要將 mapUnderscoreToCamelCase設置為true笆怠。
    自動映射甚至在特定的result map下也能工作。在這種情況下誊爹,對于每一個result map,所有的ResultSet提供的列蹬刷, 如果沒有被手工映射,則將被自動映射频丘。自動映射處理完畢后手工映射才會被處理办成。 在接下來的例子中, id 和 _userName_列將被自動映射椎镣, _hashedpassword 列將根據(jù)配置映射

3.6 cache

MyBatis 包含一個非常強大的查詢緩存特性,它可以非常方便地配置和定制诈火。MyBatis 3 中的緩存實現(xiàn)的很多改進都已經(jīng)實現(xiàn)了,使得它更加強大而且易于配置兽赁。
默認情況下是沒有開啟緩存的,除了局部的 session 緩存状答。要開啟二級緩存,你需要在你的 SQL 映射文件中添加一行:

<cache/>

字面上看就是這樣。這個簡單語句的效果如下:

  • 映射語句文件中的所有 select 語句將會被緩存刀崖。
  • 映射語句文件中的所有 insert,update 和 delete 語句會刷新緩存惊科。
  • 緩存會使用 Least Recently Used(LRU,最近最少使用的)算法來收回。
  • 根據(jù)時間表(比如 no Flush Interval,沒有刷新間隔), 緩存不會以任何時間順序 來刷新亮钦。
  • 緩存會存儲列表集合或對象(無論查詢方法返回什么)的 1024 個引用馆截。
  • 緩存會被視為是 read/write(可讀/可寫)的緩存,意味著對象檢索不是共享的,而 且可以安全地被調(diào)用者修改,而不干擾其他調(diào)用者或線程所做的潛在修改。

所有的這些屬性都可以通過緩存元素的屬性來修改蜂莉。比如:

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

這個更高級的配置創(chuàng)建了一個 FIFO 緩存,并每隔 60 秒刷新,存數(shù)結果對象或列表的 512 個引用,而且返回的對象被認為是只讀的,因此在不同線程中的調(diào)用者之間修改它們會 導致沖突蜡娶。
可用的收回策略有:

  • LRU – 最近最少使用的:移除最長時間不被使用的對象。
  • FIFO – 先進先出:按對象進入緩存的順序來移除它們映穗。
  • SOFT – 軟引用:移除基于垃圾回收器狀態(tài)和軟引用規(guī)則的對象窖张。
  • WEAK – 弱引用:更積極地移除基于垃圾收集器狀態(tài)和弱引用規(guī)則的對象。
    默認的是 LRU蚁滋。

除了這些自定義緩存的方式, 你也可以通過實現(xiàn)你自己的緩存或為其他第三方緩存方案 創(chuàng)建適配器來完全覆蓋緩存行為宿接。

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

3.7 cache-ref

也許將來的某個時候,你會想在命名空間中共享相同的緩存配置和實例赘淮。在這樣的 情況下你可以使用 cache-ref 元素來引用另外一個緩存。

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

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末睦霎,一起剝皮案震驚了整個濱河市梢卸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌副女,老刑警劉巖蛤高,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異碑幅,居然都是意外死亡襟齿,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門枕赵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來猜欺,“玉大人,你說我怎么就攤上這事拷窜】螅” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵篮昧,是天一觀的道長赋荆。 經(jīng)常有香客問我,道長懊昨,這世上最難降的妖魔是什么窄潭? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮酵颁,結果婚禮上嫉你,老公的妹妹穿的比我還像新娘。我一直安慰自己躏惋,他們只是感情好幽污,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著簿姨,像睡著了一般距误。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上扁位,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天准潭,我揣著相機與錄音,去河邊找鬼域仇。 笑死刑然,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的殉簸。 我是一名探鬼主播闰集,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼沽讹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了武鲁?” 一聲冷哼從身側響起爽雄,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎沐鼠,沒想到半個月后挚瘟,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡饲梭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年乘盖,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片憔涉。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡订框,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出兜叨,到底是詐尸還是另有隱情穿扳,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布国旷,位于F島的核電站矛物,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏跪但。R本人自食惡果不足惜履羞,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望屡久。 院中可真熱鬧忆首,春花似錦、人聲如沸涂身。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蛤售。三九已至,卻和暖如春妒潭,著一層夾襖步出監(jiān)牢的瞬間悴能,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工雳灾, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留漠酿,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓谎亩,卻偏偏與公主長得像炒嘲,于是被迫代替她去往敵國和親宇姚。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 1. 簡介 1.1 什么是 MyBatis 夫凸? MyBatis 是支持定制化 SQL浑劳、存儲過程以及高級映射的優(yōu)秀的...
    笨鳥慢飛閱讀 5,429評論 0 4
  • MyBatis 理論篇 [TOC] 什么是MyBatis ?MyBatis是支持普通SQL查詢,存儲過程和高級映射...
    有_味閱讀 2,883評論 0 26
  • SQL 映射文件幾個頂級元素(按照它們應該被定義的順序)為: cache – 給定命名空間的緩存配置。 cache...
    WesleyLien閱讀 596評論 0 0
  • 莫名其妙地拒絕友人的唱K 昨天也是 想打自己兩耳光 不守信用又懶倦的人.
    半晌清醒最好閱讀 204評論 0 1
  • 人越長大話越少 我們不再說今天受了委屈 不再說誰誰誰不理我了我好難過 我知道人和人之間沒法互相理解 大家都很忙 針...
    讀書的麥兜閱讀 221評論 0 0