Mybatis的映射文件中頂級的標簽并不多,之前有說過select
、update
、delete
、insert
、sql
等標簽,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
字段中。
查詢語句對應的resultMap
是selectUserMap1
,selectUserMap1
是繼承userBaseColumnMap
,這里繼承可以理解成java里面的繼承,也就是說繼承了父類的字段映射關系。重點看的是association
標簽,property
屬性對應的是User
類中的father
字段,表示association
內的查詢結果是封裝在father
字段中,對應的javaType
自然就是Father
,columnPrefix
表示字段的前綴,這里加上前綴是很有必要的,因為在兩個表做關聯的時候,可能會存在數據庫中字段名相同,那么在這里如果不用別名,就會導致兩個字段映射過程混亂。
嵌套查詢方式
這種方式需要關聯到另一個命名空間,上面的例子是在同一個命名空間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
標簽內也有id
、reuslt
標簽,用來做關聯類的字段映射關系,當然這個關系也可以通過一個reusltMap
表示,省去association
內寫過多的映射關系。這種reusltMap
指定可以跨命名空間。 -
column
屬性,在嵌套查詢方式的時候使用,用來給嵌套語句查詢傳遞條件,可以傳遞多個。 -
columnPrefix
屬性,在嵌套結果方式的時候使用,用來指定別名字段的前綴,可用于防止兩個表結果字段重名問題。 -
select
屬性,在嵌套查詢方式的時候使用,用來指定嵌套語句查詢的具體坐標。 -
fetchType
屬性,這個屬性有兩個可選值,分別是lazy
和eager
,分別表示是否懶加載,如果設置為懶加載,在主類中沒有使用到這個關聯對象,就會少一次關聯字句查詢的過程,只有使用的時候才去查詢。 -
javaType
屬性,指定關聯對象的具體類型。
resultMap子標簽之collection標簽
collection
和association
標簽的使用方式是相同的,也包含兩種關聯查詢方式,唯一不同的就是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
標簽里面結果涉及到兩個報告類型分別是HealthReportMale
和HealthReportFemale
,但是Person
類中可以定義兩個字段分別接收這兩種,也可以用一個字段接收,這個接收字段集合的元素類型應該定義一個父類HealthReport
,讓HealthReportMale
和HealthReportFemale
都繼承這個父類。 -
case
標簽內有resultType
和resultMap
兩種指定結果的方式,這里使用的都是resultMap
的方式,因為這種方式更優。如果使用resultType
,只能使用嵌套結果方式,然后又涉及到多張不同的表,代碼量變大,而且是重復的代碼量。這里稍微有點難理解,可以上手寫一下就知道了。 -
resultMap
的繼承關系,這里涉及到多個resultMap
,select
標簽指定的是personReportMap
,是直接resultMap
,personReportMap
繼承personBaseColumnMap
,因為人員基礎信息映射關系都在這個personBaseColumnMap
中,然后case
內的healtReportMaleMap
和healthReportFemaleMap
都是繼承personReportMap
。
Overview
習慣性的總結一下。
- 說了兩種關聯查詢方式,分別是嵌套結果和嵌套查詢,貫穿了三個標簽的使用。
-
association
標簽用于一對一的關聯查詢,主要屬性也做了說明。 -
collection
標簽用于一對多的關聯查詢,沒有具體說,可以參考association
標簽的解釋,基本相同。 -
discriminator
標簽,使用的過程最為復雜,主要的是要理清resultMap
的繼承關系。 - 這些標簽在實際開發中很少用到,現在都提倡單表操作,簡單高效,兼容分庫分表等。因此只做了解,掃知識盲點,提高面試底氣。但是
resultMap
標簽的基本使用是簡單的,而且是極其重要的。
resultMap
標簽是很重要的,甚至可以說是極其的重要,但是這些關聯查詢的功能標簽不是很重要,這個是不相悖的。
resultMap
有個很大的優點就是解耦合(阿里Java手冊也著重強調)。如果使用自動映射的話,會將數據庫列表和實體類的字段名強耦合,當數據庫列表發生修改時,實體類中的字段名也要同步修改。但是使用resultMap
指定映射關系,當有數據庫列名發生變化,只要改一下映射中的column
值即可,實體類無需任何修改變動。
本文作者:程序猿楊鮑
版權歸作者所有,轉載請注明出處