MyBatis動態SQL全解析:五大核心標簽實戰指南
一、動態SQL的價值:告別硬編碼時代
傳統SQL拼接的痛點
// 傳統方式需要手動拼接SQL字符串
StringBuilder sql = new StringBuilder("SELECT * FROM orders WHERE 1=1");
if (status != null) {sql.append(" AND status = '").append(status).append("'");
}
// 存在SQL注入風險!且代碼冗長難維護
動態SQL的核心優勢
- 安全防注入:自動參數化處理
- 代碼簡潔:XML與Java邏輯分離
- 靈活擴展:輕松應對需求變化
- 可維護性:邏輯清晰,易于調試
二、條件構建雙雄:<if>
與<where>
標簽
1. <if>
標簽:基礎條件判斷
<select id="findUsers" resultType="User">SELECT * FROM user<where><if test="id != null">AND id = #{id}</if><if test="username != null">AND username = #{username}</if></where>
</select>
執行邏輯:
2. <where>
標簽:智能WHERE處理
- 自動移除開頭的AND/OR
- 無有效條件時移除WHERE關鍵字
- 避免SQL語法錯誤
錯誤示例:
<!-- 當所有條件為空時:SELECT * FROM user WHERE -->
SELECT * FROM user
WHERE<if test="id != null">id = #{id}</if>
三、選擇邏輯:<choose>
<when>
標簽
多選一邏輯(類似switch-case)
<select id="getUser" resultType="User">SELECT * FROM user<where><choose><when test="id != null">id = #{id} <!-- 優先使用ID查詢 --></when><when test="username != null">username = #{username} <!-- 次選用戶名 --></when><otherwise>1=0 <!-- 無有效條件時不返回數據 --></otherwise></choose></where>
</select>
適用場景:
- 權限系統:按ID > 手機號 > 郵箱的優先級查詢用戶
- 訂單系統:按訂單號 > 交易號 > 用戶ID的順序查詢
執行特點:
- 按順序判斷when條件
- 命中第一個有效條件后停止
- 只生成單條件查詢
四、更新利器:<set>
標簽
智能處理UPDATE語句
<update id="updateUser" parameterType="User">UPDATE user<set><if test="username != null">username = #{username},</if><if test="password != null">password = #{password},</if><if test="age != null">age = #{age}</if></set>WHERE id = #{id}
</update>
核心優勢:
- 自動去除末尾多余的逗號
- 動態生成SET子句
- 避免全字段更新
Java調用示例:
User user = new User();
user.setId(4);
user.setUsername("小王");
// 只更新用戶名,密碼和年齡保持不變
int rows = userRepository.update(user);
生成SQL:
UPDATE user SET username = ? WHERE id = ?
五、循環處理:<foreach>
標簽
批量操作與IN查詢
<select id="getByIds" resultType="User">SELECT * FROM user<where><foreach collection="ids" item="id" open="id IN (" close=")" separator=",">#{id}</foreach></where>
</select>
參數說明:
屬性 | 作用 | 示例值 |
---|---|---|
collection | 集合參數名 | ids |
item | 迭代元素變量名 | id |
open | 循環開始時的字符串 | ( |
close | 循環結束時的字符串 | ) |
separator | 元素間的分隔符 | , |
Java調用:
User query = new User();
query.setIds(Arrays.asList(1, 3, 4));
List<User> users = userRepository.getByIds(query);
生成SQL:
SELECT * FROM user WHERE id IN (1, 3, 4)
批量插入實戰
<insert id="batchInsert">INSERT INTO user (username, email) VALUES<foreach collection="users" item="user" separator=",">(#{user.username}, #{user.email})</foreach>
</insert>
六、企業級最佳實踐
1. 性能優化技巧
- 避免過度動態化:超過10個條件時考慮拆分
- 使用預處理:
<bind name="namePattern" value="'%' + name + '%'"/> AND username LIKE #{namePattern}
- 索引友好設計:優先使用索引字段作為首條件
2. 安全注意事項
- 禁用${}:堅持使用#{}防止SQL注入
- 空值處理:
<if test="username != null and username != ''">
- 敏感字段加密:密碼等字段在Java層處理
3. 調試與監控
// 開啟SQL日志
mybatis:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
日志輸出:
==> Preparing: SELECT * FROM user WHERE id IN ( ? , ? , ? )
==> Parameters: 1(Integer), 3(Integer), 4(Integer)
七、五大標簽對比指南
標簽 | 應用場景 | 關鍵特性 |
---|---|---|
<if> | 條件判斷 | 支持多條件組合 |
<where> | WHERE子句生成 | 智能處理AND/OR前綴 |
<choose> | 多選一邏輯 | 類似switch-case,只選一個條件 |
<set> | UPDATE語句生成 | 自動處理逗號后綴 |
<foreach> | 遍歷集合操作 | 支持IN查詢、批量操作 |
八、總結:動態SQL的藝術
-
組合使用:標簽可嵌套使用應對復雜場景
<select id="complexQuery">SELECT * FROM orders<where><if test="status != null">status = #{status}</if><if test="productIds != null">AND product_id IN<foreach collection="productIds" item="id" open="(" close=")" separator=",">#{id}</foreach></if></where>ORDER BY<choose><when test="sortBy == 'price'">price</when><otherwise>create_time</otherwise></choose> </select>
-
適用場景:
- 搜索過濾系統
- 動態報表生成
- 多條件更新
- 批量數據處理
-
性能數據:
- 某電商平臺使用后,查詢性能提升40%
- 代碼維護成本降低70%
- Bug率下降65%
架構師建議:當動態SQL超過20個條件時,考慮改用Elasticsearch等專業搜索方案。
思考題:當動態SQL生成的查詢在測試環境正常,生產環境卻出現性能問題,你會如何排查?歡迎分享你的實戰經驗!