MyBatis框架淺析之 Mapper.xml 映射文件


Mapper XML 文件

MyBatis 的真正強大在於它的映射語句,也是它的魔力所在。由於它的異常強大,映射器的 XML 文件就顯得相對簡單。如果拿它跟具有相同功能的 JDBC 代碼進行對比,你會立即發現省掉了將近 95% 的代碼。MyBatis 就是針對 SQL 構建的,並且比普通的方法做的更好。

同樣需要介紹定級元素: 

mapper 

  • cache – 給定命名空間的緩存配置。
  • cache-ref – 其他命名空間緩存配置的引用。
  • resultMap – 是最復雜也是最強大的元素,用來描述如何從數據庫結果集中來加載對象。
  • parameterMap – 已廢棄!老式風格的參數映射。內聯參數是首選,這個元素可能在將來被移除,這里不會記錄。
  • sql – 可被其他語句引用的可重用語句塊。
  • insert – 映射插入語句
  • update – 映射更新語句
  • delete – 映射刪除語句
  • select – 映射查詢語句

1.mapper 根節點

有且必須只有一個屬性值:namespace, 建議設置為Mapper接口的全限定名

<mapper namespace="com.baoxian.dao.UserInfoMapper">
    ...
</mapper>

 

2.select 元素

2.1 簡單的select語句

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

解釋一下上面配置:

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

  

假如調用該條SQL傳入的 參數為 1,selectPerson(1)
原來的sql語句經過處理后相當於 SELECT * FROM PERSON WHERE ID = ?
?相當於參數的占位符
調用時傳入的 參數值 將會被替換

2.2  select元素中的所有屬性

<select
  id="selectPerson"      #必須
  parameterType="int"      #可選,寫出來會更加直觀
  parameterMap="deprecated" #已不使用
  resultType="hashmap"    #必須指定
  resultMap="personResultMap"#自定義結果集的映射
  flushCache="false"         #調用該語句是否刷新緩存,一般insert update delete 會配置成true
  useCache="true"            #
  timeout="10000"            # 語句最大等待時間 10S
  fetchSize="256"        # 
  statementType="PREPARED"
  resultSetType="FORWARD_ONLY">
Select Attributes
屬性 描述
id 在命名空間中唯一的標識符,可以被用來引用這條語句。
parameterType 將會傳入這條語句的參數類的完全限定名或別名。這個屬性是可選的,因為 MyBatis 可以通過 TypeHandler 推斷出具體傳入語句的參數,默認值為 unset。
parameterMap 這是引用外部 parameterMap 的已經被廢棄的方法。使用內聯參數映射和 parameterType 屬性。
resultType 從這條語句中返回的期望類型的類的完全限定名或別名。注意如果是集合情形,那應該是集合可以包含的類型,而不能是集合本身。使用 resultType 或 resultMap,但不能同時使用。
resultMap 外部 resultMap 的命名引用。結果集的映射是 MyBatis 最強大的特性,對其有一個很好的理解的話,許多復雜映射的情形都能迎刃而解。使用 resultMap 或 resultType,但不能同時使用。
flushCache 將其設置為 true,任何時候只要語句被調用,都會導致本地緩存和二級緩存都會被清空,默認值:false。
useCache 將其設置為 true,將會導致本條語句的結果被二級緩存,默認值:對 select 元素為 true。
timeout 這個設置是在拋出異常之前,驅動程序等待數據庫返回請求結果的秒數。默認值為 unset(依賴驅動)。
fetchSize 這是嘗試影響驅動程序每次批量返回的結果行數和這個設置值相等。默認值為 unset(依賴驅動)。
statementType STATEMENT,PREPARED 或 CALLABLE 的一個。這會讓 MyBatis 分別使用 Statement,PreparedStatement 或 CallableStatement,默認值:PREPARED。
resultSetType FORWARD_ONLY,SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 中的一個,默認值為 unset (依賴驅動)。
databaseId 如果配置了 databaseIdProvider,MyBatis 會加載所有的不帶 databaseId 或匹配當前 databaseId 的語句;如果帶或者不帶的語句都有,則不帶的會被忽略。
resultOrdered 這個設置僅針對嵌套結果 select 語句適用:如果為 true,就是假設包含了嵌套結果集或是分組了,這樣的話當返回一個主結果行的時候,就不會發生有對前面結果集的引用的情況。這就使得在獲取嵌套的結果集的時候不至於導致內存不夠用。默認值:false
resultSets 這個設置僅對多結果集的情況適用,它將列出語句執行后返回的結果集並每個結果集給一個名稱,名稱是逗號分隔的。

 

2.3 insert ,update,delete

<insert
  id="insertAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  keyProperty=""      #將JDBC生成的主鍵值 賦值給keyProperty對應的屬性名
  keyColumn=""
  useGeneratedKeys="" #取出JDBC生成的主鍵
  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">

 

屬性 描述
id 命名空間中的唯一標識符,可被用來代表這條語句。
parameterType 將要傳入語句的參數的完全限定類名或別名。這個屬性是可選的,因為 MyBatis 可以通過 TypeHandler 推斷出具體傳入語句的參數,默認值為 unset。
parameterMap 這是引用外部 parameterMap 的已經被廢棄的方法。使用內聯參數映射和 parameterType 屬性。
flushCache 將其設置為 true,任何時候只要語句被調用,都會導致本地緩存和二級緩存都會被清空,默認值:true(對應插入、更新和刪除語句)。
timeout 這個設置是在拋出異常之前,驅動程序等待數據庫返回請求結果的秒數。默認值為 unset(依賴驅動)。
statementType STATEMENT,PREPARED 或 CALLABLE 的一個。這會讓 MyBatis 分別使用 Statement,PreparedStatement 或 CallableStatement,默認值:PREPARED。
useGeneratedKeys (僅對 insert 和 update 有用)這會令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法來取出由數據庫內部生成的主鍵(比如:像 MySQL 和 SQL Server 這樣的關系數據庫管理系統的自動遞增字段),默認值:false。
keyProperty (僅對 insert 和 update 有用)唯一標記一個屬性,MyBatis 會通過 getGeneratedKeys 的返回值或者通過 insert 語句的 selectKey 子元素設置它的鍵值,默認:unset。如果希望得到多個生成的列,也可以是逗號分隔的屬性名稱列表。
keyColumn (僅對 insert 和 update 有用)通過生成的鍵值設置表中的列名,這個設置僅在某些數據庫(像 PostgreSQL)是必須的,當主鍵列不是表中的第一列的時候需要設置。如果希望得到多個生成的列,也可以是逗號分隔的屬性名稱列表。
databaseId 如果配置了 databaseIdProvider,MyBatis 會加載所有的不帶 databaseId 或匹配當前 databaseId 的語句;如果帶或者不帶的語句都有,則不帶的會被忽略。

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>

如果設置了主鍵自增,則上述insert語句可以更改為:

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

如果數據庫支持多行插入(例如MySQL數據庫),則insert語句可以修改為:

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

其中foreach 會對集合進行迭代 , item 表示集合中每個元素,collection 表示集合的類型, separator表示分隔符,並且智能的在最后一個元素后去除分隔符

 

如果數據庫不支持自動生成主鍵,則可以使用另外一種 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>

上述語句的執行順序是,先執行自動生成隨機ID的語句, 並將隨機生成的值轉成int類型 再通過set方法 設置到Author的id屬性中,從而執行插入時,id屬性值將對應數據庫的id列,插入到數據庫中

selectKey 的重要屬性如下:

<selectKey
  keyProperty="id"
  resultType="int"
  order="BEFORE" 
  statementType="PREPARED">
屬性 描述
keyProperty selectKey 語句結果應該被設置的目標屬性。如果希望得到多個生成的列,也可以是逗號分隔的屬性名稱列表。
keyColumn 匹配屬性的返回結果集中的列名稱。如果希望得到多個生成的列,也可以是逗號分隔的屬性名稱列表。
resultType 結果的類型。MyBatis 通常可以推算出來,但是為了更加確定寫上也不會有什么問題。MyBatis 允許任何簡單類型用作主鍵的類型,包括字符串。如果希望作用於多個生成的列,則可以使用一個包含期望屬性的 Object 或一個 Map。
order 這可以被設置為 BEFORE 或 AFTER。如果設置為 BEFORE,那么它會首先選擇主鍵,設置 keyProperty 然后執行插入語句。如果設置為 AFTER,那么先執行插入語句,然后是 selectKey 元素 - 這和像 Oracle 的數據庫相似,在插入語句內部可能有嵌入索引調用。
statementType 與前面相同,MyBatis 支持 STATEMENT,PREPARED 和 CALLABLE 語句的映射類型,分別代表 PreparedStatement 和 CallableStatement 類型。

 

2.4 sql

sql元素可以用來定義可復用的sql代碼:

<sql id="fromClause">
    /*通過${}取調用者傳遞的屬性值*/
    from ${tableName}
</sql>

使用<include>元素調用定義的sql元素,以下是簡單的實現了復用的效果,往往實際中會更加復雜:

<!--通過ID查找用戶-->
  <select id="selectUserById" resultMap="BaseResultMap" parameterType="java.lang.Long" >
    select id,userName,age
    /*通過property屬性 向sql元素動態傳遞參數值,兩邊規定好property的屬性名稱即可*/
    <include refid="fromClause">
      <property name="tableName" value="userinfo"/>
    </include>
    where id = #{id}
  </select>

  <!--查詢出所有用戶-->
  <select id="selectAll" resultMap="BaseResultMap" >
    select id, username,age
    <include refid="fromClause">
      <property name="tableName" value="userinfo"/>
    </include>
  </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>

2.5 參數 parameters

sql語句接收的參數有兩種類型,一種是基本類型,另一種是復雜的對象類型

如果sql語句 接收的參數是一個對象.那么傳遞過來的對象要提供相應的getter方法,此時mybatis才能正確的取出屬性值

例如:

<!--通過ID更新用戶-->
  <update id="updateByPrimaryKey" parameterType="com.baoxian.domain.UserInfo" >
    update UserInfo
    set username = #{userName},age = #{age}
    where id = #{id}
  </update>

每一個參數都支持為其指定特殊的類型

格式如下:

<!--通過ID更新用戶-->
  <update id="updateByPrimaryKey" parameterType="com.baoxian.domain.UserInfo" >
    update UserInfo
    set username = #{userName},age = #{age,javaType=int,jdbcType=NUMERIC}
    where id = #{id}
  </update>

其中age參數,指明java類型是int , jdbcType是NUMERIC

如果傳遞一個為null的參數值到數據庫中,並且該列允許為null,那么必須為該參數指定javaType

除了javaType,與 jdbcType之外,還可以指定以下:

typeHandler: 需要特殊類型處理器處理參數的映射 (幾乎不用這么做)
numericScale:如果參數值是一個小數,希望保存進數據庫的值 保留小數點后兩位 ,則可以通這樣實現: #{salary,javaType=double,jdbcType=NUMERIC,numericScale=2}

mode:該屬性有三個可選值,IN,OUT,及INOUT;如果參數為 OUT 或 INOUT,參數對象屬性的真實值將會被改變,就像你在獲取輸出參數時所期望的那樣。

如果 mode 為 OUT(或 INOUT),而且 jdbcType 為 CURSOR(也就是 Oracle 的 REFCURSOR),你必須指定一個 resultMap 來映射結果集 ResultMap 到參數類型。
要注意這里的 javaType 屬性是可選的,如果留空並且 jdbcType 是 CURSOR,它會被自動地被設為 ResultMap

 

說了那么多,其實更多時候,我們90%的情況是不需要為參數指定什么額外屬性,MyBatis已經幫你做好該做的

最多在java參數為null時,指定javaType即可

 

2.6 字符串替換

使用#{} 會生成一個預處理參數,提高了SQL的安全性, 但有時不希望對參數值進行轉義,保持參數原本的值,則可以使用 ${}進行取值

但是${} 會引起SQL注入的問題

 

2.7 resultMap

具體說resultMap之間先說下resultType

正常情況,如果我們僅僅是需要查詢的結果,那么將resultType設置成 map即可

但是很多時候,我們希望將表中查詢出的數據封裝成Java對象,此時只需要將resultType 設置為相應類的全限定名 或 別名即可;

假設實體類如下,已提供相應的setter getter方法

表的設計如下:

 

 此時表中的字段名與實體類中的屬性名一致, 使用resultType, myBatis 會自動將對應列的值封裝進同名的屬性中;

 

但是並不是任何情況下,列名都能與java的屬性名對應上.此時我們將userInfo表中的userName列名修改為user_name

那遇到這種不匹配的情況怎么去解決呢?

 

方式一:在select語句中對查詢出的列使用別名 ,保證別名與java屬性名一致即可,例如:

  <select id="selectUserById" resultType="UserInfo" parameterType="java.lang.Long" >
    select id,user_name as username ,age
    from userInfo
    where id = #{id}
  </select>

方式二:使用resultMap,自定義查詢結果集中列到java屬性的映射關系:

<resultMap id="userResultMap" type="userinfo">
    <result column="user_name" property="userName"/>
  </resultMap>
<select id="selectUserById" resultMap="userResultMap" parameterType="java.lang.Long" >
  select id,user_name as username ,age
  from userInfo
  where id = #{id}
</select>

 

此時發現兩種解決方式,都是差不多,甚至方式1可能更簡單一些.沒錯,對於簡單的sql語句與javaBean.確實只需要使用方式一即可.

 

但是如果以下情況出現,你還會覺得方式一好嗎?

1.表的列數與java的字段有多個,大多數列名與屬性名都不能完全對應

2.在情景1的情況下,同一個Mapper.xml文件中有多處select語句,需要將結果映射到java對象中,此時每個select語句都寫大量重復的別名

   會造成工作量的重復

3.如果對象的屬性不全為簡單類型,屬性可能也是對象. 例如:Employee 類中,包含屬性 department.此時通過別名是無法完成映射的

 

對於上述情景2的情況,定義一次resultMap,可以在多處使用.不會造成同樣語句的多次書寫,同時resultMap也可以像情景3中那樣,完成復雜的映射關系

那么就來介紹一下resultMap元素:

2.7.1 resultMap的屬性

<resultMap id="userResultMap" type="userinfo" autoMapping="true">
      <id column="id" property="id"  />
      <result column="age" property="age"  />
      <result column="user_name" property="userName"/>
  </resultMap>

 

id:  當前命名空間中的一個唯一標識,用於標識一個result map.

type: resultMap需要映射的目標Java對象的類型,可以是類的全限定名 也可以是別名

autoMapping :  兩種值:true/false 是否開啟自動列名 到 屬性名的自動映射,默認開啟.(true)

 

2.7.2 resultMap的子元素

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

寫在前面,在resultMap中,使用最多的元素有:id,result,association,collection , 不常用的有 constructor 與 discriminator

constructor 

public class User {
   //...
   public User(Integer id, String username, int age) {
     //...
  }
//...
}

此時可以通過constructor元素,將值注入到User對象中:

<constructor>
   <idArg column="id" javaType="int"/>
   <arg column="username" javaType="String"/>
   <arg column="age" javaType="_int"/>
</constructor>

但是必須要嚴格按照構造器的參數順序注入

myBatis 3.4.3版本開始后,可以指定構造器中的參數名稱,此時可以不用按照順序書寫

<constructor>
   <idArg column="id" javaType="int" name="id" />
   <arg column="age" javaType="_int" name="age" />
   <arg column="username" javaType="String" name="username" />
</constructor>

 

id元素:標識主鍵列 與對象唯一標識時使用

result元素:非主鍵,基本類型的屬性使用

association元素:當屬性也是一個對象類型時使用

  association元素中的屬性值介紹:

  property:需要注入的屬性名稱

  javaType:需要注入的屬性類型

  

  #其中 select 與 column 一同用於額外sql查詢時

  select:需要額外執行的sql語句 (nameSpace.selectName)

  column:發送額外sql語句時,該SQL語句的所需的參數 

  

  #以下四個屬性,當需要聯表查詢注入子對象屬性時,才會使用

  resultMap:需要使用的結果集映射

  columnPrefix:同一位resultMap中的column列 指定前綴

  notNullColumn:該屬性指定的列名不可為空,如果那一列為空,則該子對象不創建,可以指定對個.默認不創建

  autoMapping:是否開啟自動同名映射

collection元素:當屬性是集合類型時使用:

  collection除了ofType屬性代替了javaType外,其他屬性值與association屬性值,幾乎使用方法完全一致

  ofType:表示集合中的元素類型 可以為類的全限定名 或者 類別名

 

 

現在有Employee 與 Department java類,同時數據庫中存在相應的表

public class Employee {
    private long id;
    private String eName;
    private Department department;

    //getter...

    //setter...
}
public class Department {
    private long id;
    private String dName;
    private List<Employee> employees;

    //getter...

    //setter...
}

employee表:

 

 department表:

 

 

 

 

如果想在查詢Employee對象時 ,不僅查詢出基本類型的id,e_name,還想額外注入department屬性怎么做呢?

此時可以使用resultMap中的association 元素完成department屬性的注入,實現的方式有兩種:

 

方式一:通過發送額外SQL語句的方式,根據結果集中的dept_id ,查詢出department對象:

<resultMap id="employeeResultMap" type="employee">
    <id column="e_id" property="id"/>
    <result column="e_name" property="eName"/>
    <association property="department" column="dept_id" javaType="department" select="com.baoxian.dao.DepartmentMapper.selectDepartmentById"/>
  </resultMap>

<!--根據e_id查詢員工-->
  <select id="selectEmployeeById" resultMap="employeeResultMap" parameterType="java.lang.Long" >
    select e_id,e_name,dept_id
    from employee
    where e_id = #{id}
  </select>

 

 

此時查看額外sql語句 selectDepartmentById:

<resultMap id="departResultMap" type="department" autoMapping="false">
    <id column="d_id" property="id"/>
    <result column="d_name" property="dName"/>
  </resultMap>

<!--根據d_id查詢部門_供employee使用-->
  <select id="selectDepartmentById" resultMap="departResultMap" parameterType="java.lang.Long" >
    select d_id ,d_name
    from department
    where d_id = #{id}
  </select>

 

方式二:通過聯表的方式,將結果集中的內容分別注入到employee 與department中:

非常重要: id元素在嵌套結果映射中扮演着非 常重要的角色。你應該總是指定一個或多個可以唯一標識結果的屬性。實際上如果你不指定它的話, MyBatis仍然可以工作,但是會有嚴重的性能問題。在可以唯一標識結果的情況下, 盡可能少的選擇屬性。主鍵是一個顯而易見的選擇(即使是復合主鍵)。

<resultMap id="emp_ResultMap" type="employee">
    <id column="e_id" property="id"/>
    <result column="e_name" property="eName"/>
    <association property="department" javaType="department" columnPrefix="d_" >
      <id column="id" property="id"/>
      <result column="name" property="dName"/>
    </association>
  </resultMap>

  <!--根據e_id 關聯employee 與 department表查詢-->
  <select id="queryEmployeeById" resultMap="emp_ResultMap" parameterType="java.lang.Long" >
    select
          e_id,
          e_name,
          dept_id as deptId,
          d_id,
          d_name
    from employee
    left join department
    on employee.dept_id = department.d_id
    where e_id = #{id}
  </select>

association元素 通過resultMap屬性可以指定一個定義好的resultMap,從而替換子元素Id,type,如下:

<resultMap id="emp_ResultMap" type="employee">
    <id column="e_id" property="id"/>
    <result column="e_name" property="eName"/>
    <association property="department" resultMap="com.baoxian.dao.DepartmentMapper.dept_resultMap" columnPrefix="d_" />
  </resultMap>

<!--在departmentMapper.xml中-->
<resultMap id="dept_resultMap" type="department"  >
    <id column="id" property="id"/>
    <result column="name" property="dName"/>
  </resultMap>

 

通過上述我們可知,發送額外SQL的方式 與 聯表查詢都可以完成 對象屬性的注入,下面來介紹下兩者的優缺點:

發送額外SQL:

  優點:直觀,select語句可以復用

  缺點:會出現N + 1 的問題

  

  何為N + 1 ? 

  個人認為應該讀作 1+ N , 就根據上述的Employee查詢,每次查詢出1條employee記錄,都要額外的發送1條sql

  如果1條sql語句中,查詢出N條employee記錄, 那么就要額外的發送N條sql語句,查詢部門的信息

  此時本想執行1條,卻額外多執行了N條sql語句 , 這就是 N + 1的問題

   

  可以通過懶加載的方式,盡可能的減少N + 1問題:

  解決方式1:在mybatis的配置文件中,配置lazyLoadingEnabled 當使用到關聯對象的任意屬性時,才會發送額外SQL查詢:

<settings>
        <setting name="lazyLoadingEnabled" value="true"/>
</settings>

  解決方式2:同樣在mybaits中,配置aggressiveLazyLoading,只有當明確訪問對象的某一屬性時,才會發送額外SQL查詢:

<setting name="aggressiveLazyLoading" value="false"/>

 

聯表查詢:

  優點:沒有N + 1的問題

  缺點:聯表也是非常耗時的操作

 

嵌套查詢與發送額外SQL查詢,兩種方式如何進行選擇?
  1.當One方比較多時,例如:部門比較多,此時由於N+1的情況的存在,選擇嵌套查詢的方式會更好一些
  2.當One方比較少時,同時嵌套查詢關聯的字段比較多,因為關聯表是非常消耗性能,此時因為一級緩存的特點,使用發送額外SQL的方式會更好一些

collection元素:

當對象中的屬性類型為集合時,就要使用到colleciton元素進行映射

同樣可以使用發送額外SQL與 聯表查詢 實現 collection的映射:

 

發送額外SQL:

<resultMap id="departmentResultMap" type="department">
    <id column="d_id" property="id"/>
    <result column="d_name" property="dName"/>
    <collection property="employees" column="d_id" ofType="employee" select="com.baoxian.dao.EmployeeMapper.selectEmployeeByDeptId"/>
  </resultMap>

<select id="selectDepartById" resultMap="departmentResultMap" parameterType="java.lang.Long" >
    select d_id ,d_name
    from department
    where d_id = #{id}
  </select>

<!--EmployeeMapper.xml文件中-->
<!--根據部門id查詢該部門下所有員工-->
  <select id="selectEmployeeByDeptId" resultMap="employeeResultMap" parameterType="java.lang.Long" >
    select e_id,e_name
    from employee
    where dept_id = #{id}
  </select>

 

聯表查詢:

<resultMap id="department_ResultMap" type="department">
    <id column="d_id" property="id"/>
    <result column="d_name" property="dName"/>
    <collection property="employees"  ofType="employee" columnPrefix="e_">
      <id column="id" property="id"/>
      <result column="name" property="eName"/>
    </collection>
  </resultMap>

<select id="selectDepartmentAndEmployeeById" resultMap="department_ResultMap" parameterType="java.lang.Long" >
    select
      d_id ,
      d_name,
      e_id,
      e_name,
      dept_id
    from department
    left join employee on department.d_id = employee.dept_id
    where d_id = #{id}
  </select>

聯表查詢,同樣也可以指定collection中的 resultMap屬性,在此不做演示,參考association元素

 

discriminator 鑒別器元素

簡單的說,非常類似java中的switch語句:

<resultMap id="vehicleResult" type="Vehicle">
  <id property="id" column="id" />
  <result property="vin" column="vin"/>
  <result property="year" column="year"/>
  <result property="make" column="make"/>
  <result property="model" column="model"/>
  <result property="color" column="color"/>
  <discriminator javaType="int" column="vehicle_type">
    <case value="1" resultType="carResult">
      <result property="doorCount" column="door_count" />
    </case>
    <case value="2" resultType="truckResult">
      <result property="boxSize" column="box_size" />
      <result property="extendedCab" column="extended_cab" />
    </case>
    <case value="3" resultType="vanResult">
      <result property="powerSlidingDoor" column="power_sliding_door" />
    </case>
    <case value="4" resultType="suvResult">
      <result property="allWheelDrive" column="all_wheel_drive" />
    </case>
  </discriminator>
</resultMap>

當結果集中列或者列的別名,vehicle_type值為1時,選擇相應的resultType 並加載各自的result

 

自動映射

當自動映射查詢結果時,MyBatis會獲取sql返回的列名並在java類中查找相同名字的屬性(忽略大小寫)。 這意味着如果Mybatis發現了ID列和id屬性,Mybatis會將ID的值賦給id

通常數據庫列使用大寫單詞命名,單詞間用下划線分隔;而java屬性一般遵循駝峰命名法。 為了在這兩種命名方式之間啟用自動映射,需要將 mapUnderscoreToCamelCase設置為true。

即:

<setting name="mapUnderscoreToCamelCase" value="true"/>

對於resultMap,如果沒有被手動映射的列,將會被自動映射,自動映射處理完后手動映射才會處理,因此resultMap可以這么寫:

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

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

自動映射的配置:

<setting name="autoMappingBehavior" value="PARTIAL"/>

值有三種可選:NONE, PARTIAL, FULL

NONE: 表示取消自動映射;

PARTIAL: 只會自動映射沒有定義嵌套結果集映射的結果集。

FULL : 會自動映射任意復雜的結果集(無論是否嵌套)。

當使用FULL時,自動映射會在處理join結果時執行,並且join取得若干相同行的不同實體數據,因此這可能導致非預期的映射。

通過設置select元素的autoMapping屬性值 true / false 決定當前resultMap是否啟用自動映射

例如:

<resultMap id="userResultMap" type="User" autoMapping="false">
  <result property="password" column="hashed_password"/>
</resultMap>

 

至此最重要的resultMap元素已徹底結束;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM