ORM框架之Mybatis(四):映射文件resultMap標簽詳解

Mybatis的映射文件中頂級的標簽并不多,之前有說過selectupdatedeleteinsertsql等標簽,resultMap在之前的文章也有提過,但是當時也是簡單的提過,其實這個標簽里面的內容很多,可簡單可復雜。正常開發中簡單的基本都在使用,但是涉及到復雜的用的就少啦。

association實現一對一的查詢,collection實現一對多的查詢,discriminator實現鑒別器的功能,也就是根據不同的條件實現不同的關聯查詢。為什么說用到這些復雜的內容很少呢。以前在項目上性能要求不是很高,對分庫分表概念沒有那么高的時候,采用關聯查詢其實沒有什么不好,但是現在互聯網發展迅速,數據量變得龐大,查詢SQL的性能要求也變得更高。關聯查詢在大數據量的時候執行效率不高也就格外的凸顯出來了。如果看過阿里的Java開發手冊可以知道,他們是不推薦關聯查詢,而是提倡單表操作,如果需要查其他表信息,就根據條件,操作其他單表。

雖然關聯查詢操作變少,但是面試的時候還是可以用來問的。不管從哪一個方面來說擴展知識面都不是壞事。

resultMap子標簽之association標簽

在說association標簽的具體使用前,先說一下兩種關聯查詢方式。

  • 嵌套結果:使用嵌套結果映射來處理重復的聯合結果的子集
  • 嵌套查詢:通過執行另外一個 SQL 映射語句來返回預期的復雜類型

咋一看上面的兩個概念很難理解,下面用代碼體現一下。

嵌套結果方式
//用戶類
public class User{
    private Integer id;
    private String user_name;
    private String phone;
    private String email;
    private Father father;
}
//父親類
public class Father{
    private Integer id;
    private String name;
    private Integer age;
}
<!--用戶表基礎字段的映射關系-->
<resultMap id="userBaseColumnMap" type="com.zdydoit.core.model.User">
    <id property="id" column="id"/>
    <result property="userName" column="user_name"/>
    <result property="phone" column="phone"/>
    <result property="email" column="email"/>
</resultMap>
<!-- 嵌套結果方式 -->
<resultMap id="selectUserMap1" extends="userBaseColumnMap" type="com.zdydoit.core.model.User">
    <association property="father" javaType="com.zdydoit.core.model.Father" columnPrefix="tf_">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="age" column="age"/>
    </association>
</resultMap>
<!--查詢語句-->
<select id="slectUser1" resultMap="selectUserMap1" parameterType="java.lang.Integer">
    select
    tu.id,
    tu.user_name,
    tu.phone,
    tu.email,
    tu.father_id,
    tf.id tf_id,
    tf.name tf_name,
    tf.age tf_age
    from t_user tu,t_father tf
    where tu.id = #{id} and tu.father_id = tf.id
</select>

用戶表中有一個字段father,對應的是Father實體類,每人都有一個父親,而且是一對一的關系,至于那些大干爹、二干爹這里不做考慮。查詢出來的結果用戶信息的字段自然封裝到User中,那父親的信息就是放在father字段中。

查詢語句對應的resultMapselectUserMap1selectUserMap1是繼承userBaseColumnMap,這里繼承可以理解成java里面的繼承,也就是說繼承了父類的字段映射關系。重點看的是association標簽,property屬性對應的是User類中的father字段,表示association內的查詢結果是封裝在father字段中,對應的javaType自然就是FathercolumnPrefix表示字段的前綴,這里加上前綴是很有必要的,因為在兩個表做關聯的時候,可能會存在數據庫中字段名相同,那么在這里如果不用別名,就會導致兩個字段映射過程混亂。

嵌套查詢方式

這種方式需要關聯到另一個命名空間,上面的例子是在同一個命名空間com.zdydoit.core.mapper.UserMapper下。這里的另一個命名空間就是com.zdydoit.core.mapper.FatherMapper

實體類的代碼和上面的一樣,下面只做映射關系的展示。

com.zdydoit.core.mapper.UserMapper命名空間下:

<!-- 嵌套查詢方式 -->
<resultMap id="selectUserMap2" type="com.zdydoit.core.model.User">
    <association property="father" column="father_id" select="com.zdydoit.core.mapper.FatherMapper.selectById"/>
</resultMap>
<!-- 查詢語句 -->
<select id="selectUser2" resultMap="selectUserMap2" parameterType="java.lang.Integer">
    select
    id,
    user_name,
    phone,
    email,
    father_id
    from t_user
    where id = #{id}
</select>

com.zdydoit.core.mapper.FatherMapper命名空間下:

<resultMap id="fatherBaseColumnMap" type="com.zdydoit.core.model.Father">
    <id property="id" column="id"/>
    <result property="name" column="name" />
    <result property="age" column="age" />
</resultMap>
<!-- 單表查詢 -->
<select id="selectById" resultMap="fatherBaseColumnMap">
    select
    id,
    name,
    age
    from t_father
    where id = #{father_id}
</select>

這樣查詢的過程稍微有點繞。首先是對t_user表執行單表查詢操作,查詢的結果中有一個字段father_id,然后就是association標簽將這個字段作為條件傳給com.zdydoit.core.mapper.FatherMapper命名空間,執行另一個單表查詢操作selectById查詢Father的信息。和之前的區別就是,之前是一個SQL執行關聯查詢,現在是用多個單表查詢組合得到查詢結果。

這里只有一個查詢條件father_id,如果有多個寫法就按下面的寫法。

<!-- 
這里旨在說明column多參數的寫法
'rel_'開頭的值表示SQL語句里面查詢出的字段名
等號左邊的表示在selectInfo中引用的字段名稱 
-->
<association property = "father" colum="{id = rel_id,age = rel_age,nickname = rel_nickname}" select="com.zdydoit.mapper.XxxMapper.selectInfo">

association標簽的屬性很多,下面總結一下常用標簽的含義。

  • property屬性,這個屬性是必須的,對應的是實體類中的字段,和result標簽的property屬性是相同含義的。
  • resultMap屬性,association標簽內也有idreuslt標簽,用來做關聯類的字段映射關系,當然這個關系也可以通過一個reusltMap表示,省去association內寫過多的映射關系。這種reusltMap指定可以跨命名空間。
  • column屬性,在嵌套查詢方式的時候使用,用來給嵌套語句查詢傳遞條件,可以傳遞多個。
  • columnPrefix屬性,在嵌套結果方式的時候使用,用來指定別名字段的前綴,可用于防止兩個表結果字段重名問題。
  • select屬性,在嵌套查詢方式的時候使用,用來指定嵌套語句查詢的具體坐標。
  • fetchType屬性,這個屬性有兩個可選值,分別是lazyeager,分別表示是否懶加載,如果設置為懶加載,在主類中沒有使用到這個關聯對象,就會少一次關聯字句查詢的過程,只有使用的時候才去查詢。
  • javaType屬性,指定關聯對象的具體類型。

resultMap子標簽之collection標簽

collectionassociation標簽的使用方式是相同的,也包含兩種關聯查詢方式,唯一不同的就是association只能對應一條記錄,是一對一的關系,使用單個類字段來接收,collection可以對應多條記錄,是一對多的關系,接收使用集合方式。

還有就是collection多了一個ofType字段,這個字段是用來指定集合內元素的類型,如下:

private List<Computer> computers;

這個是有ofType對應的類型就是Computer。同樣理解javaType對應的類型就是List。不過這里一般都不會手動去指定javaType,會自動映射到集合中。

resultMap子標簽之discriminator標簽

這個應用場景感覺不多,不過還是可以舉個栗子理解一下的。

在做體檢的時候,男性和女性體檢內容是有區別的。現在有三張表,分別是人員表、男性體檢報告表、女性體檢報告表。要求查一個人的體檢報告。那很簡單是當這個人是男性的時候,查詢其體檢報告是從男性體檢報告表中查詢,反之就到女性體檢報告表中查詢。實現方式如下:

<!-- 人員基礎信息 -->
<resultMap id="personBaseColumnMap" type="Person">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="gender" column="gender"/>
</resultMap>

<!-- 查詢女性體檢報告 -->
<resultMap id="healthReportFemaleMap" extends="personReportMap" type="Person">
    <collection property="healthReports" column="id" select="com.zdydoit.mapper.HealthFemaleMapper.selectByPersonId"/>
</resultMap>

<!-- 查詢男性體檢報告 -->
<resultMap id="healthReportMaleMap" extends="personReportMap" type="Person">
    <collection property="healthReports" column="id" select="com.zdydoit.mapper.HealthMaleMapper.selectByPersonId"/>
</resultMap>

<!-- 查詢人員信息+體檢報告 -->
<resultMap id="personReportMap" extends="personBaseColumnMap" type="Person">
    <discriminator javaType="java.lang.Integer" column="gender">
        <case value="1" resultMap="healthReportMaleMap"/>
        <case value="2" resultMap="healthReportFemaleMap"/>
    </discriminator>
</resultMap>

<!-- 查詢語句 -->
<select id="selectPersonReport" resultMap="personReportMap" parameterType="java.lang.Integer">
    select id,name,gender from t_person where id = #{id}
</select>
    

有幾點需要說一下。

  • case標簽里面結果涉及到兩個報告類型分別是HealthReportMaleHealthReportFemale,但是Person類中可以定義兩個字段分別接收這兩種,也可以用一個字段接收,這個接收字段集合的元素類型應該定義一個父類HealthReport,讓HealthReportMaleHealthReportFemale都繼承這個父類。
  • case標簽內有resultTyperesultMap兩種指定結果的方式,這里使用的都是resultMap的方式,因為這種方式更優。如果使用resultType,只能使用嵌套結果方式,然后又涉及到多張不同的表,代碼量變大,而且是重復的代碼量。這里稍微有點難理解,可以上手寫一下就知道了。
  • resultMap的繼承關系,這里涉及到多個resultMapselect標簽指定的是personReportMap,是直接resultMappersonReportMap繼承personBaseColumnMap,因為人員基礎信息映射關系都在這個personBaseColumnMap中,然后case內的healtReportMaleMaphealthReportFemaleMap都是繼承personReportMap

Overview

習慣性的總結一下。

  • 說了兩種關聯查詢方式,分別是嵌套結果和嵌套查詢,貫穿了三個標簽的使用。
  • association標簽用于一對一的關聯查詢,主要屬性也做了說明。
  • collection標簽用于一對多的關聯查詢,沒有具體說,可以參考association標簽的解釋,基本相同。
  • discriminator標簽,使用的過程最為復雜,主要的是要理清resultMap的繼承關系。
  • 這些標簽在實際開發中很少用到,現在都提倡單表操作,簡單高效,兼容分庫分表等。因此只做了解,掃知識盲點,提高面試底氣。但是resultMap標簽的基本使用是簡單的,而且是極其重要的。

resultMap標簽是很重要的,甚至可以說是極其的重要,但是這些關聯查詢的功能標簽不是很重要,這個是不相悖的。

resultMap有個很大的優點就是解耦合(阿里Java手冊也著重強調)。如果使用自動映射的話,會將數據庫列表和實體類的字段名強耦合,當數據庫列表發生修改時,實體類中的字段名也要同步修改。但是使用resultMap指定映射關系,當有數據庫列名發生變化,只要改一下映射中的column值即可,實體類無需任何修改變動。

本文作者:程序猿楊鮑
版權歸作者所有,轉載請注明出處

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,963評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,348評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,083評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,706評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,442評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,802評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,795評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,983評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,542評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,287評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,486評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,030評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,710評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,116評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,412評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,224評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,462評論 2 378

推薦閱讀更多精彩內容

  • 1. 簡介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存儲過程以及高級映射的優秀的...
    笨鳥慢飛閱讀 5,569評論 0 4
  • title: MyBatis之使用resultMap實現高級映射tags: MyBatiscategories: ...
    codingXiaxw閱讀 1,534評論 1 1
  • MyBatis 理論篇 [TOC] 什么是MyBatis ?MyBatis是支持普通SQL查詢,存儲過程和高級映射...
    有_味閱讀 2,933評論 0 26
  • SQL 映射文件的頂級元素(按照它們應該被定義的順序): cache – 給定命名空間的緩存配置。 cache-r...
    悠揚前奏閱讀 720評論 0 0
  • 周末上班,帶著大寶貝值班,我想讓他在我單位安安靜靜的做作業,抽空中午帶孩子出去玩一趟,這兩天和寶貝關系不是很好,我...
    愛你的貝貝閱讀 328評論 3 8