《Mybatis從入門到精通》讀書筆記(三)
第六章. Mybatis高階查詢
在關係型資料庫中,我們經常要處理一對一,一對多的關係。在面對這種關係的時候,我們可能要寫多個方法分別查詢這些資料,然後再組合到一起。這種處理方式特別適合用在大型系統上,由於分庫分表,這種用法可以減少表之間的關聯查詢,方便系統進行擴充套件。但是在一般的企業應用中,使用Mybatis的高階結果對映便可以輕鬆地處理這種一對一、一對多的關係。
6.1. 高階結果對映
6.1.1. 一對一對映
1. 使用自動對映處理一對一關係
使用自動對映就是通過別名讓Mybatis自動將值匹配到對應的欄位上,簡單的別名對映如user_name對應userName。除此之外Mybatis還支援複雜的屬性對映,可以多層巢狀,例如將role.role_name對映到role.roleName上。Mybatis會先查詢role屬性,如果存在role屬性就建立role物件,然後在role物件中繼續查詢roleName,將role_name的值繫結到role物件的roleName屬性上。
<select id="selectUserAndRoleById" resultType="tk.mybatis.simple.model.SysUser"> select u.id, u.user_name userName, u.user_password userPassword, u.user_email userEmail, u.user_info userInfo, u.head_img headImg, u.create_time createTime, r.id "role.id", r.role_name "role.roleName", r.enabled "role.enabled", r.create_by "role.createBy", r.create_time "role.createTime" from sys_user u inner join sys_user_role ur on u.id = ur.user_id inner join sys_role r on ur.role_id = r.id where u.id = #{id} </select>
注意上述方法中sys_role查詢列的別名都是“role.”字首,通過這種方式將role的屬性都對映到了SysUser的role屬性上。
2. 使用resultMap的association標籤配置一對一對映
<!-- <cache-ref namespace="tk.mybatis.simple.mapper.RoleMapper"/> --> <resultMap id="userMap" type="tk.mybatis.simple.model.SysUser"> <id property="id" column="id"/> <result property="userName" column="user_name"/> <result property="userPassword" column="user_password"/> <result property="userEmail" column="user_email"/> <result property="userInfo" column="user_info"/> <result property="headImg" column="head_img" jdbcType="BLOB"/> <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/> </resultMap> <resultMap id="userRoleMap" extends="userMap" type="tk.mybatis.simple.model.SysUser"> <!-- 2. 直接引用RoleMapper裡的resultMap --> <association property="role" columnPrefix="role_" resultMap="tk.mybatis.simple.mapper.RoleMapper.roleMap"/> <!-- 1. 直接羅列出來sys_role表字段和SysRole屬性的對映關係 --> <!--<association property="role" columnPrefix="role_" javaType="tk.mybatis.simple.model.SysRole"> <id property="id" column="id"/> <result property="roleName" column="role_name"/> <result property="enabled" column="enabled"/> <result property="createBy" column="create_by"/> <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/> </association>--> </resultMap>
<mapper namespace="tk.mybatis.simple.mapper.RoleMapper">
<resultMap id="roleMap" type="tk.mybatis.simple.model.SysRole">
<id property="id" column="id"/>
<result property="roleName" column="role_name"/>
<result property="enabled" column="enabled"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
</resultMap>
</mapper>
association標籤包含包含以下屬性:
- property:對應實體類中的屬性名,必填項
- javaType:屬性對應的Java型別
- resultMap:可以直接使用現有的resultMap,而不需要在這裡配置
- columnPrefix:查詢列的字首,配置字首後,在子標籤配置result的column時可以省略字首
3. association標籤的巢狀查詢
上面介紹的都屬於巢狀的結果對映,需要關聯多個表將所有需要的值一次性查詢出來。這種方式的好處是減少資料庫查詢次數,減輕資料庫的壓力,缺點是要寫很複雜的SQL,並且當巢狀結果更復雜時,不容易一次寫正確,由於要在應用伺服器上將結果對映到不同的類上,因此也會增加應用伺服器的壓力。當一次會使用到巢狀結果,並且整個複雜的SQL執行速度很快時,建議使用關聯的巢狀結果對映。
除了這兩種通過複雜的SQL查詢獲取結果,還可以利用簡單的SQL通過多次查詢轉化為我們需要的結果,這種方式與根據業務邏輯手動執行多次SQL的方式很像,最後會將結果組合成一個物件。
association標籤的巢狀查詢常用的屬性如下:
- select:另一個對映查詢的id,Mybatis會額外執行這個查詢獲取巢狀物件的結果。
- column:列名(別名)將住查詢中列的結果作為巢狀查詢的引數,配置方式如column={prop1=col1,prop2=col2},prop1和prop2將作為巢狀查詢的引數。
- fetchType:資料載入方式,可選值為lazy和eager,分別為延遲載入和積極載入,這個配置會覆蓋全域性的lazyLoadingEnabled配置
UserMapper.xml配置:
<resultMap id="userRoleMapSelect" extends="userMap" type="tk.mybatis.simple.model.SysUser">
<association property="role"
fetchType="lazy"
select="tk.mybatis.simple.mapper.RoleMapper.selectRoleById"
column="{id=role_id}"/>
</resultMap>
<select id="selectUserAndRoleByIdSelect" resultMap="userRoleMapSelect">
select
u.id,
u.user_name,
u.user_password,
u.user_email,
u.user_info,
u.head_img,
u.create_time,
ur.role_id
from sys_user u
inner join sys_user_role ur on u.id = ur.user_id
where u.id = #{id}
</select>
RoleMapper.xml配置:
<select id="selectRoleById" resultMap="roleMap">
select * from sys_role where id = #{id}
</select>
3.1. 實現延遲載入
lazyLoadingEnabled | 延遲載入的全域性開關。當開啟時,所有關聯物件都會延遲載入。 特定關聯關係中可通過設定fetchType屬性來覆蓋該項的開關狀態。 | true | false | false |
aggressiveLazyLoading | 當開啟時,任何方法的呼叫都會載入該物件的所有屬性。當關閉時,每個屬性會按需載入(參考lazyLoadTriggerMethods). | true | false | false (true in ≤3.4.1) |
lazyLoadTriggerMethods | 指定哪個物件的方法觸發一次延遲載入。 | 用逗號分隔的方法列表。 | equals,clone,hashCode,toString |
首先全域性mybatis-config.xml關閉aggressiveLazyLoading:
<settings>
<!-- 其他配置 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
其次在
<resultMap id="userRoleMapSelect" extends="userMap" type="tk.mybatis.simple.model.SysUser">
<association property="role"
fetchType="lazy"
select="tk.mybatis.simple.mapper.RoleMapper.selectRoleById"
column="{id=role_id}"/>
</resultMap>