MyBatis 配置復(fù)用從入門到精通
MyBatis 配置復(fù)用詳解:從入門到精通
引言
在實(shí)際開發(fā)中,MyBatis 的 XML 配置文件往往會(huì)隨著業(yè)務(wù)復(fù)雜度提升而急劇膨脹。大量的重復(fù) SQL 和 resultMap 不僅難以維護(hù),還容易引入錯(cuò)誤。本文將深入探討 MyBatis 的各種配置復(fù)用技巧,幫助您寫出更優(yōu)雅、更易維護(hù)的持久層代碼。
一、SQL 片段復(fù)用
SQL 片段復(fù)用是最基礎(chǔ)也是最常用的復(fù)用方式,適用于重復(fù)的列名、查詢條件、排序分頁等場景。
1.1 基礎(chǔ) SQL 片段定義與引用
<mapper namespace="com.example.mapper.UserMapper">
<!-- 定義常用列名片段 -->
<sql id="Base_Column_List">
id, username, password, email, phone,
status, create_time, update_time
</sql>
<!-- 定義查詢條件片段 -->
<sql id="Where_Clause">
<where>
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="email != null and email != ''">
AND email = #{email}
</if>
<if test="status != null">
AND status = #{status}
</if>
<if test="startTime != null">
AND create_time >= #{startTime}
</if>
<if test="endTime != null">
AND create_time <= #{endTime}
</if>
</where>
</sql>
<!-- 引用片段 -->
<select id="selectByCondition" resultType="User">
SELECT
<include refid="Base_Column_List"/>
FROM user
<include refid="Where_Clause"/>
</select>
</mapper>1.2 帶參數(shù)的 SQL 片段
SQL 片段支持參數(shù)傳遞,使其更加靈活:
<!-- 定義帶表別名的列片段 -->
<sql id="Alias_Columns">
${alias}.id, ${alias}.username, ${alias}.email, ${alias}.create_time
</sql>
<!-- 定義動(dòng)態(tài)表名片段 -->
<sql id="Dynamic_Table">
${tableName} ${alias}
</sql>
<!-- 定義連接條件片段 -->
<sql id="Join_Condition">
${mainAlias}.${mainField} = ${joinAlias}.${joinField}
</sql>
<!-- 使用參數(shù)化片段 -->
<select id="selectUserWithOrders" resultMap="UserOrderMap">
SELECT
<include refid="Alias_Columns">
<property name="alias" value="u"/>
</include>,
o.id as order_id, o.order_no, o.amount
FROM
<include refid="Dynamic_Table">
<property name="tableName" value="user"/>
<property name="alias" value="u"/>
</include>
LEFT JOIN order o ON
<include refid="Join_Condition">
<property name="mainAlias" value="u"/>
<property name="mainField" value="id"/>
<property name="joinAlias" value="o"/>
<property name="joinField" value="user_id"/>
</include>
WHERE u.id = #{id}
</select>1.3 條件片段的高級(jí)用法
<!-- 定義多種條件片段 -->
<sql id="Basic_Conditions">
<if test="deleted != null">
AND deleted = #{deleted}
</if>
<if test="enabled != null">
AND enabled = #{enabled}
</if>
</sql>
<sql id="Time_Range_Condition">
<if test="timeField != null and startTime != null">
AND ${timeField} >= #{startTime}
</if>
<if test="timeField != null and endTime != null">
AND ${timeField} <= #{endTime}
</if>
</sql>
<sql id="Pagination">
<if test="offset != null and limit != null">
LIMIT #{offset}, #{limit}
</if>
<if test="pageNum != null and pageSize != null">
LIMIT #{pageNum}, #{pageSize}
</if>
</sql>
<!-- 組合使用 -->
<select id="searchUsers" resultType="User">
SELECT * FROM user
<where>
<include refid="Basic_Conditions"/>
<include refid="Time_Range_Condition">
<property name="timeField" value="create_time"/>
</include>
<if test="keyword != null">
AND (username LIKE CONCAT('%', #{keyword}, '%')
OR email LIKE CONCAT('%', #{keyword}, '%'))
</if>
</where>
ORDER BY create_time DESC
<include refid="Pagination"/>
</select>1.4 SQL 片段的嵌套使用
<!-- 定義基礎(chǔ)片段 -->
<sql id="User_Fields">
id, username, email
</sql>
<sql id="User_Base_Query">
SELECT <include refid="User_Fields"/> FROM user
</sql>
<!-- 嵌套構(gòu)建復(fù)雜查詢 -->
<sql id="Active_User_Query">
<include refid="User_Base_Query"/>
WHERE status = 1 AND last_login_time > DATE_SUB(NOW(), INTERVAL 30 DAY)
</sql>
<sql id="Vip_User_Query">
<include refid="User_Base_Query"/>
WHERE vip_level > 0
</sql>
<!-- 使用 -->
<select id="countActiveVipUsers" resultType="int">
SELECT COUNT(*) FROM (
<include refid="Active_User_Query"/>
UNION
<include refid="Vip_User_Query"/>
) AS combined
</select>二、resultMap 復(fù)用
resultMap 復(fù)用是處理復(fù)雜對(duì)象映射的關(guān)鍵技術(shù),可以避免重復(fù)定義字段映射關(guān)系。
2.1 基礎(chǔ)繼承復(fù)用
<!-- 基礎(chǔ)映射定義 -->
<resultMap id="BaseResultMap" type="com.example.entity.User">
<id property="id" column="id" jdbcType="BIGINT"/>
<result property="username" column="username" jdbcType="VARCHAR"/>
<result property="password" column="password" jdbcType="VARCHAR"/>
<result property="email" column="email" jdbcType="VARCHAR"/>
<result property="phone" column="phone" jdbcType="VARCHAR"/>
<result property="status" column="status" jdbcType="INTEGER"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
</resultMap>
<!-- 繼承并擴(kuò)展關(guān)聯(lián)對(duì)象 -->
<resultMap id="UserWithRoleMap" extends="BaseResultMap" type="com.example.entity.User">
<!-- 一對(duì)一關(guān)聯(lián) -->
<association property="role" javaType="com.example.entity.Role">
<id property="id" column="role_id"/>
<result property="name" column="role_name"/>
<result property="code" column="role_code"/>
<result property="description" column="role_desc"/>
</association>
</resultMap>
<!-- 繼承并擴(kuò)展集合對(duì)象 -->
<resultMap id="UserWithOrdersMap" extends="BaseResultMap" type="com.example.entity.User">
<!-- 一對(duì)多集合 -->
<collection property="orders" ofType="com.example.entity.Order"
notNullColumn="order_id">
<id property="id" column="order_id"/>
<result property="orderNo" column="order_no"/>
<result property="amount" column="amount"/>
<result property="status" column="order_status"/>
<result property="createTime" column="order_create_time"/>
</collection>
</resultMap>
<!-- 多層繼承 -->
<resultMap id="UserFullInfoMap" extends="UserWithRoleMap" type="com.example.entity.User">
<collection property="permissions" ofType="com.example.entity.Permission">
<id property="id" column="permission_id"/>
<result property="name" column="permission_name"/>
<result property="code" column="permission_code"/>
</collection>
<!-- 添加額外屬性 -->
<result property="lastLoginIp" column="last_login_ip"/>
<result property="lastLoginTime" column="last_login_time"/>
</resultMap>2.2 引用外部 resultMap
<!-- 定義可復(fù)用的關(guān)聯(lián)映射(可在其他文件中) -->
<mapper namespace="com.example.mapper.RoleMapper">
<resultMap id="RoleMap" type="com.example.entity.Role">
<id property="id" column="role_id"/>
<result property="name" column="role_name"/>
<result property="code" column="role_code"/>
<result property="permissions" column="id"
select="com.example.mapper.PermissionMapper.selectByRoleId"/>
</resultMap>
</mapper>
<!-- 在當(dāng)前文件中引用 -->
<mapper namespace="com.example.mapper.UserMapper">
<resultMap id="UserWithRoleRefMap" extends="BaseResultMap"
type="com.example.entity.User">
<!-- 引用外部 resultMap,注意列別名要匹配 -->
<association property="role"
resultMap="com.example.mapper.RoleMapper.RoleMap"
columnPrefix="role_"/>
<!-- 也可以使用 select 方式引用 -->
<collection property="roles"
column="id"
select="com.example.mapper.RoleMapper.selectByUserId"/>
</resultMap>
<!-- 對(duì)應(yīng)的查詢 -->
<select id="selectUserWithRole" resultMap="UserWithRoleRefMap">
SELECT
u.*,
r.id as role_id,
r.name as role_name,
r.code as role_code
FROM user u
LEFT JOIN user_role ur ON u.id = ur.user_id
LEFT JOIN role r ON ur.role_id = r.id
WHERE u.id = #{id}
</select>
</mapper>2.3 使用 columnPrefix 實(shí)現(xiàn)同一結(jié)構(gòu)的復(fù)用
<!-- 定義地址映射 -->
<resultMap id="AddressMap" type="com.example.entity.Address">
<result property="province" column="province"/>
<result property="city" column="city"/>
<result property="district" column="district"/>
<result property="street" column="street"/>
<result property="detail" column="detail"/>
<result property="zipCode" column="zip_code"/>
</resultMap>
<!-- 在多個(gè)地方復(fù)用,通過 columnPrefix 區(qū)分不同地址 -->
<resultMap id="UserWithAddressesMap" type="com.example.entity.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<!-- 注冊(cè)地址,使用 reg_ 前綴 -->
<association property="registerAddress"
resultMap="AddressMap"
columnPrefix="reg_"/>
<!-- 居住地址,使用 live_ 前綴 -->
<association property="livingAddress"
resultMap="AddressMap"
columnPrefix="live_"/>
<!-- 工作地址,使用 work_ 前綴 -->
<association property="workAddress"
resultMap="AddressMap"
columnPrefix="work_"/>
<!-- 收貨地址列表,每個(gè)地址使用不同的前綴 -->
<collection property="shippingAddresses"
ofType="com.example.entity.Address"
resultMap="AddressMap"
columnPrefix="ship_"/>
</resultMap>
<!-- 對(duì)應(yīng)的查詢 -->
<select id="selectUserWithAllAddresses" resultMap="UserWithAddressesMap">
SELECT
u.id, u.username,
ra.province as reg_province,
ra.city as reg_city,
ra.district as reg_district,
ra.street as reg_street,
ra.detail as reg_detail,
ra.zip_code as reg_zip_code,
la.province as live_province,
la.city as live_city,
la.district as live_district,
la.street as live_street,
la.detail as live_detail,
la.zip_code as live_zip_code,
wa.province as work_province,
wa.city as work_city,
wa.district as work_district,
wa.street as work_street,
wa.detail as work_detail,
wa.zip_code as work_zip_code,
sa.province as ship_province,
sa.city as ship_city,
sa.district as ship_district,
sa.street as ship_street,
sa.detail as ship_detail,
sa.zip_code as ship_zip_code
FROM user u
LEFT JOIN address ra ON u.register_address_id = ra.id
LEFT JOIN address la ON u.living_address_id = la.id
LEFT JOIN address wa ON u.work_address_id = wa.id
LEFT JOIN user_shipping_address usa ON u.id = usa.user_id
LEFT JOIN address sa ON usa.address_id = sa.id
WHERE u.id = #{id}
</select>2.4 構(gòu)造方法映射復(fù)用
<!-- 定義帶構(gòu)造方法的映射 -->
<resultMap id="ConstructorMap" type="com.example.entity.User">
<constructor>
<idArg column="id" javaType="Long"/>
<arg column="username" javaType="String"/>
<arg column="email" javaType="String"/>
<arg column="create_time" javaType="java.time.LocalDateTime"/>
</constructor>
</resultMap>
<!-- 繼承構(gòu)造方法映射 -->
<resultMap id="UserWithRoleConstructorMap" extends="ConstructorMap"
type="com.example.entity.User">
<association property="role" javaType="com.example.entity.Role">
<id property="id" column="role_id"/>
<result property="name" column="role_name"/>
</association>
</resultMap>
<!-- 使用不同的構(gòu)造參數(shù)順序 -->
<resultMap id="AlternativeConstructorMap" type="com.example.entity.User">
<constructor>
<arg column="username" javaType="String"/>
<arg column="email" javaType="String"/>
<idArg column="id" javaType="Long"/>
</constructor>
</resultMap>2.5 鑒別器映射復(fù)用
<!-- 定義基礎(chǔ)映射 -->
<resultMap id="BaseMessageMap" type="com.example.entity.Message">
<id property="id" column="id"/>
<result property="content" column="content"/>
<result property="createTime" column="create_time"/>
</resultMap>
<!-- 使用鑒別器實(shí)現(xiàn)多態(tài)查詢 -->
<resultMap id="MessageMap" extends="BaseMessageMap"
type="com.example.entity.Message">
<discriminator javaType="int" column="message_type">
<!-- 文本消息 -->
<case value="1" resultMap="TextMessageMap"/>
<!-- 圖片消息 -->
<case value="2" resultMap="ImageMessageMap"/>
<!-- 語音消息 -->
<case value="3" resultMap="VoiceMessageMap"/>
<!-- 視頻消息,繼承基礎(chǔ)映射并擴(kuò)展 -->
<case value="4" resultType="com.example.entity.VideoMessage">
<result property="videoUrl" column="video_url"/>
<result property="duration" column="duration"/>
<result property="thumbnail" column="thumbnail"/>
</case>
</discriminator>
</resultMap>
<!-- 各子類映射可以繼續(xù)繼承 -->
<resultMap id="TextMessageMap" extends="BaseMessageMap"
type="com.example.entity.TextMessage">
<result property="textFormat" column="text_format"/>
</resultMap>
<resultMap id="ImageMessageMap" extends="BaseMessageMap"
type="com.example.entity.ImageMessage">
<result property="imageUrl" column="image_url"/>
<result property="width" column="width"/>
<result property="height" column="height"/>
</resultMap>三、高級(jí)復(fù)用技巧
3.1 通過繼承復(fù)用完整查詢
<!-- 基礎(chǔ)查詢 -->
<select id="selectBaseUsers" resultMap="BaseResultMap">
SELECT id, username, email, status
FROM user
WHERE status = 1
</select>
<!-- 繼承基礎(chǔ)查詢并擴(kuò)展條件 -->
<select id="selectActiveUsers" resultMap="BaseResultMap"
extends="selectBaseUsers">
AND last_login_time > #{lastLoginTime}
</select>
<!-- 繼承并修改排序 -->
<select id="selectRecentActiveUsers" resultMap="BaseResultMap"
extends="selectActiveUsers">
ORDER BY last_login_time DESC
LIMIT #{limit}
</select>
<!-- 多層繼承 -->
<select id="selectVipActiveUsers" resultMap="UserWithRoleMap"
extends="selectActiveUsers">
AND vip_level > 0
AND EXISTS (SELECT 1 FROM user_role WHERE user_id = user.id)
</select>3.2 泛型基礎(chǔ)映射抽象
<!-- 定義抽象的基礎(chǔ)映射(不指定具體類型) -->
<resultMap id="BaseEntityMap" type="com.example.entity.BaseEntity">
<id property="id" column="id"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
<result property="createBy" column="create_by"/>
<result property="updateBy" column="update_by"/>
<result property="deleted" column="deleted" javaType="boolean"/>
</resultMap>
<!-- 子類繼承基礎(chǔ)映射 -->
<resultMap id="ProductMap" extends="BaseEntityMap"
type="com.example.entity.Product">
<result property="name" column="name"/>
<result property="price" column="price"/>
<result property="stock" column="stock"/>
</resultMap>
<resultMap id="OrderMap" extends="BaseEntityMap"
type="com.example.entity.Order">
<result property="orderNo" column="order_no"/>
<result property="amount" column="amount"/>
<result property="userId" column="user_id"/>
</resultMap>
<resultMap id="CategoryMap" extends="BaseEntityMap"
type="com.example.entity.Category">
<result property="name" column="name"/>
<result property="parentId" column="parent_id"/>
<result property="level" column="level"/>
</resultMap>3.3 動(dòng)態(tài)映射組合
<!-- 定義可選的映射片段 -->
<sql id="Optional_User_Fields">
<if test="includeProfile">
, profile, avatar
</if>
<if test="includeStats">
, login_count, last_login_ip
</if>
</sql>
<!-- 動(dòng)態(tài)構(gòu)建 resultMap 的引用 -->
<resultMap id="DynamicUserMap" type="com.example.entity.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<!-- 基礎(chǔ)字段始終包含 -->
</resultMap>
<resultMap id="UserWithProfileMap" extends="DynamicUserMap"
type="com.example.entity.User">
<result property="profile" column="profile"/>
<result property="avatar" column="avatar"/>
</resultMap>
<resultMap id="UserWithStatsMap" extends="DynamicUserMap"
type="com.example.entity.User">
<result property="loginCount" column="login_count"/>
<result property="lastLoginIp" column="last_login_ip"/>
</resultMap>
<!-- 根據(jù)條件動(dòng)態(tài)選擇 resultMap -->
<select id="selectUserDynamic" resultMap="DynamicUserMap">
SELECT id, username
<include refid="Optional_User_Fields"/>
FROM user
WHERE id = #{id}
</select>3.4 類型處理器復(fù)用
<!-- 自定義類型處理器 -->
<typeHandler handler="com.example.handler.JsonTypeHandler"
javaType="com.example.entity.JsonObject"/>
<typeHandler handler="com.example.handler.EnumTypeHandler"
javaType="com.example.enums.StatusEnum"/>
<!-- 在 resultMap 中復(fù)用 -->
<resultMap id="ProductDetailMap" type="com.example.entity.Product">
<id property="id" column="id"/>
<result property="name" column="name"/>
<!-- 復(fù)用 JSON 處理器 -->
<result property="attributes" column="attributes"
typeHandler="com.example.handler.JsonTypeHandler"/>
<result property="specs" column="specs"
typeHandler="com.example.handler.JsonTypeHandler"/>
<!-- 復(fù)用枚舉處理器 -->
<result property="status" column="status"
typeHandler="com.example.handler.EnumTypeHandler"/>
<result property="category" column="category"
typeHandler="com.example.handler.EnumTypeHandler"/>
</resultMap>
<!-- 在多個(gè)映射中復(fù)用相同的處理器配置 -->
<resultMap id="OrderExtrasMap" type="com.example.entity.Order">
<result property="extras" column="extras"
typeHandler="com.example.handler.JsonTypeHandler"/>
<result property="status" column="status"
typeHandler="com.example.handler.EnumTypeHandler"/>
</resultMap>四、最佳實(shí)踐與注意事項(xiàng)
4.1 命名規(guī)范建議
<!-- 使用清晰、一致的命名規(guī)范 -->
<mapper namespace="com.example.mapper.UserMapper">
<!-- SQL 片段命名:功能 + 類型 -->
<sql id="Base_Column_List"/> <!-- 基礎(chǔ)列名 -->
<sql id="Base_Where_Clause"/> <!-- 基礎(chǔ)條件 -->
<sql id="Refined_Where_Clause"/> <!-- 精確條件 -->
<sql id="Fuzzy_Where_Clause"/> <!-- 模糊條件 -->
<sql id="Order_By_Clause"/> <!-- 排序 -->
<sql id="Pagination_Clause"/> <!-- 分頁 -->
<!-- resultMap 命名:實(shí)體 + 功能 + Map -->
<resultMap id="BaseResultMap"/> <!-- 基礎(chǔ)映射 -->
<resultMap id="UserWithRoleMap"/> <!-- 帶角色 -->
<resultMap id="UserDetailMap"/> <!-- 詳細(xì)信息 -->
<resultMap id="UserLazyMap"/> <!-- 懶加載 -->
<!-- 查詢命名:操作 + 條件 + 結(jié)果類型 -->
<select id="selectById"/>
<select id="selectByCondition"/>
<select id="selectWithRoleById"/>
<select id="selectPage"/>
</mapper>4.2 性能考慮
<!-- 1. 懶加載配置 -->
<resultMap id="UserLazyMap" type="com.example.entity.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<!-- 設(shè)置懶加載 -->
<association property="role"
select="com.example.mapper.RoleMapper.selectByUserId"
column="id"
fetchType="lazy"/>
<collection property="orders"
select="com.example.mapper.OrderMapper.selectByUserId"
column="id"
fetchType="lazy"/>
</resultMap>
<!-- 2. 使用延遲加載的全局配置 -->
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
<!-- 3. 批量查詢優(yōu)化 -->
<resultMap id="UserWithOrdersBatchMap" type="com.example.entity.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<!-- 使用批量查詢 -->
<collection property="orders"
ofType="com.example.entity.Order"
select="com.example.mapper.OrderMapper.selectByUserIds"
column="{userIds=id}"/>
</resultMap>4.3 常見問題與解決方案
<!-- 問題1:循環(huán)引用導(dǎo)致棧溢出 -->
<!-- 解決方案:合理設(shè)置遞歸深度或使用延遲加載 -->
<resultMap id="CategoryTreeMap" type="com.example.entity.Category">
<id property="id" column="id"/>
<result property="name" column="name"/>
<!-- 設(shè)置遞歸查詢,但要有終止條件 -->
<collection property="children"
ofType="com.example.entity.Category"
select="selectChildren"
column="{parentId=id, level=level+1}"/>
</resultMap>
<!-- 問題2:字段沖突 -->
<!-- 解決方案:使用別名和 columnPrefix -->
<resultMap id="UserProductMap" type="com.example.entity.UserProduct">
<id property="userId" column="user_id"/>
<id property="productId" column="product_id"/>
<!-- 使用別名區(qū)分同名字段 -->
<association property="user" resultMap="UserMap" columnPrefix="u_"/>
<association property="product" resultMap="ProductMap" columnPrefix="p_"/>
</resultMap>
<!-- 問題3:N+1 查詢問題 -->
<!-- 解決方案:使用聯(lián)表查詢代替嵌套查詢 -->
<resultMap id="UserWithOrdersJoinMap" type="com.example.entity.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<!-- 使用聯(lián)表查詢 -->
<collection property="orders" ofType="com.example.entity.Order">
<id property="id" column="order_id"/>
<result property="orderNo" column="order_no"/>
<result property="amount" column="amount"/>
</collection>
</resultMap>
<!-- 對(duì)應(yīng)的優(yōu)化查詢 -->
<select id="selectUserWithOrders" resultMap="UserWithOrdersJoinMap">
SELECT
u.*,
o.id as order_id,
o.order_no,
o.amount
FROM user u
LEFT JOIN order o ON u.id = o.user_id
WHERE u.id = #{id}
</select>五、實(shí)際項(xiàng)目應(yīng)用示例
5.1 通用 CRUD 復(fù)用框架
<!-- BaseMapper.xml - 基礎(chǔ)映射文件 -->
<mapper namespace="com.example.mapper.BaseMapper">
<!-- 通用字段 -->
<sql id="Base_Column">
id, create_time, update_time, create_by, update_by, deleted
</sql>
<!-- 通用條件:未刪除 -->
<sql id="Not_Deleted">
AND deleted = 0
</sql>
<!-- 通用 resultMap -->
<resultMap id="BaseResultMap" type="com.example.entity.BaseEntity">
<id property="id" column="id"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
<result property="createBy" column="create_by"/>
<result property="updateBy" column="update_by"/>
<result property="deleted" column="deleted"/>
</resultMap>
<!-- 通用插入 -->
<sql id="Base_Insert">
INSERT INTO ${tableName} (
<include refid="Base_Column"/>
<if test="extraFields != null">
, ${extraFields}
</if>
) VALUES (
#{id}, #{createTime}, #{updateTime}, #{createBy}, #{updateBy}, #{deleted}
<if test="extraValues != null">
, ${extraValues}
</if>
)
</sql>
<!-- 通用更新 -->
<sql id="Base_Update">
UPDATE ${tableName}
<set>
update_time = NOW(),
<if test="updateBy != null">update_by = #{updateBy},</if>
${updateFields}
</set>
WHERE id = #{id}
</sql>
</mapper>
<!-- UserMapper.xml - 具體實(shí)現(xiàn) -->
<mapper namespace="com.example.mapper.UserMapper">
<!-- 繼承通用 resultMap -->
<resultMap id="UserMap" extends="com.example.mapper.BaseMapper.BaseResultMap"
type="com.example.entity.User">
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="email" column="email"/>
<result property="phone" column="phone"/>
<result property="status" column="status"/>
</resultMap>
<!-- 使用通用 SQL 片段 -->
<select id="selectById" resultMap="UserMap">
SELECT
<include refid="com.example.mapper.BaseMapper.Base_Column"/>,
username, password, email, phone, status
FROM user
WHERE id = #{id}
<include refid="com.example.mapper.BaseMapper.Not_Deleted"/>
</select>
<!-- 復(fù)雜查詢 -->
<select id="selectUserDetail" resultMap="UserDetailMap">
SELECT
u.*,
r.id as role_id,
r.name as role_name,
r.code as role_code,
d.id as dept_id,
d.name as dept_name
FROM user u
LEFT JOIN user_role ur ON u.id = ur.user_id
LEFT JOIN role r ON ur.role_id = r.id
LEFT JOIN department d ON u.dept_id = d.id
WHERE u.id = #{id}
<include refid="com.example.mapper.BaseMapper.Not_Deleted"/>
</select>
</mapper>5.2 多租戶系統(tǒng)復(fù)用
<!-- 租戶隔離的基礎(chǔ)配置 -->
<mapper namespace="com.example.mapper.TenantBaseMapper">
<!-- 租戶字段 -->
<sql id="Tenant_Column">
tenant_id
</sql>
<!-- 租戶條件 -->
<sql id="Tenant_Condition">
AND tenant_id = #{tenantId}
</sql>
<!-- 自動(dòng)注入租戶ID -->
<sql id="Auto_Set_Tenant">
<if test="tenantId == null">
#{_parameter.getTenantId()} as tenant_id,
</if>
<if test="tenantId != null">
#{tenantId} as tenant_id,
</if>
</sql>
<!-- 租戶基礎(chǔ) resultMap -->
<resultMap id="TenantBaseMap" extends="com.example.mapper.BaseMapper.BaseResultMap">
<result property="tenantId" column="tenant_id"/>
</resultMap>
</mapper>
<!-- 業(yè)務(wù)表繼承租戶配置 -->
<resultMap id="BusinessMap" extends="com.example.mapper.TenantBaseMapper.TenantBaseMap"
type="com.example.entity.Business">
<!-- 業(yè)務(wù)特有字段 -->
<result property="name" column="name"/>
<result property="code" column="code"/>
</resultMap>
<!-- 查詢時(shí)自動(dòng)帶上租戶條件 -->
<select id="selectByBusiness" resultMap="BusinessMap">
SELECT * FROM business
<where>
<include refid="com.example.mapper.TenantBaseMapper.Tenant_Condition"/>
<if test="name != null">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
</where>
</select>六、總結(jié)
6.1 復(fù)用方式對(duì)比
| 復(fù)用方式 | 適用場景 | 優(yōu)點(diǎn) | 缺點(diǎn) |
|---|---|---|---|
| SQL 片段 | 重復(fù)的 SQL 代碼塊 | 簡單直觀,參數(shù)靈活 | 僅限于 SQL 層面 |
| resultMap 繼承 | 實(shí)體映射復(fù)用 | 面向?qū)ο螅С侄鄳B(tài) | 繼承層級(jí)過深難維護(hù) |
| columnPrefix | 同一結(jié)構(gòu)多字段 | 避免重復(fù)定義 | 查詢語句較復(fù)雜 |
| 引用外部 resultMap | 跨文件復(fù)用 | 模塊化好,解耦 | 需注意命名空間 |
| 查詢繼承 | 相似查詢復(fù)用 | 減少重復(fù) SQL | 調(diào)試相對(duì)困難 |
6.2 最佳實(shí)踐建議
- 合理粒度:SQL 片段粒度不宜過細(xì),避免過度拆分
- 命名規(guī)范:建立統(tǒng)一的命名規(guī)范,提高可讀性
- 適度繼承:resultMap 繼承層級(jí)建議不超過 3 層
- 性能考慮:注意 N+1 問題,合理使用聯(lián)表查詢
- 注釋完善:關(guān)鍵復(fù)用點(diǎn)添加注釋,說明用途和使用方式
- 版本控制:復(fù)用配置變更時(shí)注意影響范圍
6.3 注意事項(xiàng)
- 作用域:同 namespace 內(nèi)可直接引用,跨 namespace 需使用全限定名
- 參數(shù)傳遞:
<include>標(biāo)簽的<property>只在當(dāng)前片段有效 - 優(yōu)先級(jí):extends 的 SQL 會(huì)在當(dāng)前 SQL 之前執(zhí)行
- 循環(huán)依賴:避免 resultMap 循環(huán)引用導(dǎo)致棧溢出
- 調(diào)試難度:過度復(fù)用可能增加調(diào)試難度,需要權(quán)衡
通過合理運(yùn)用這些復(fù)用技巧,可以顯著提高 MyBatis 配置的可維護(hù)性,減少重復(fù)代碼,提升開發(fā)效率。在實(shí)際項(xiàng)目中,建議根據(jù)具體場景選擇合適的復(fù)用方式,并建立團(tuán)隊(duì)統(tǒng)一的規(guī)范。
到此這篇關(guān)于MyBatis 配置復(fù)用從入門到精通的文章就介紹到這了,更多相關(guān)mybatis 配置復(fù)用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot?緩存預(yù)熱的實(shí)現(xiàn)
本文主要介紹了SpringBoot?緩存預(yù)熱的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2007-11-11
Java實(shí)現(xiàn)Excel表單控件的添加與刪除
本文通過Java代碼示例介紹如何在Excel表格中添加表單控件,包括文本框、單選按鈕、復(fù)選框、組合框、微調(diào)按鈕等,以及如何刪除Excel中的指定表單控件,需要的可以參考一下2022-05-05
Spring使用注解進(jìn)行對(duì)象裝配(DI)的操作
獲取Bean對(duì)象也叫做對(duì)象裝配,就是把對(duì)象取出來放到某個(gè)類中,有時(shí)候也叫對(duì)象注入,這篇文章主要介紹了Spring使用注解進(jìn)行對(duì)象裝配(DI),需要的朋友可以參考下2023-07-07
java中的this引用及對(duì)象構(gòu)造初始化
這篇文章主要介紹了java中的this引用及對(duì)象構(gòu)造初始化,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-08-08
Spring Boot日志技術(shù)logback原理及配置解析
這篇文章主要介紹了Spring Boot日志技術(shù)logback原理及用法解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07

