MyBatis實戰(zhàn)之動態(tài)生成SQL詳解
在電商平臺的用戶管理模塊中,需要面對多種不同的用戶查詢組合條件。當使用傳統(tǒng)的硬編碼SQL方式時,代碼膨脹到了2000多行,維護成本極高。而引入MyBatis動態(tài)SQL后,同樣的功能僅用300行代碼實現,且可讀性提升了3倍——這就是動態(tài)SQL的威力!
一、為什么需要動態(tài)SQL
傳統(tǒng)SQL拼接的痛點
// 傳統(tǒng)方式需要手動拼接SQL字符串
StringBuilder sql = new StringBuilder("SELECT * FROM user WHERE 1=1");
if (user.getId() != null) {
sql.append(" AND id = ").append(user.getId());
}
if (user.getUsername() != null) {
sql.append(" AND username = '").append(user.getUsername()).append("'");
}
// 存在SQL注入風險!且代碼冗長難維護
動態(tài)SQL的核心價值
- 消除重復代碼:相同業(yè)務邏輯不再需要重復編寫
- 防止SQL注入:自動使用預編譯參數
- 提升可讀性:SQL與Java邏輯解耦
- 降低出錯率:避免手動拼接錯誤
二、動態(tài)SQL核心標簽解析
1.<if>標簽:條件分支處理
業(yè)務場景:根據傳入參數動態(tài)添加查詢條件
<select id="findUsers" parameterType="User" resultType="User">
SELECT * FROM user
<where>
<if test="id != null">
AND id = #{id}
</if>
<if test="username != null and username != ''">
AND username = #{username}
</if>
<if test="password != null">
AND password = #{password}
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
</select>
執(zhí)行原理:

2.<where>標簽:智能WHERE處理
自動處理:
- 條件前多余的AND/OR
- 當所有條件都不滿足時,移除WHERE關鍵字
錯誤示例:
<!-- 當所有if都不滿足時,SQL會變成:SELECT * FROM user WHERE -->
SELECT * FROM user
WHERE
<if test="id != null">id = #{id}</if>
正確用法:
SELECT * FROM user
<where>
<if test="id != null">id = #{id}</if>
<!-- 其他條件... -->
</where>
3. 其他核心標簽
| 標簽 | 應用場景 | 示例片段 |
|---|---|---|
| <choose> | 多選一邏輯(類似switch-case) | <when test="...">...<otherwise>... |
| <foreach> | 遍歷集合(IN查詢等) | item in #{ids} open="(" close=")" |
| <set> | 動態(tài)更新語句 | 更新時自動處理SET后的逗號 |
| <trim> | 自定義字符串修剪 | 可替代where/set的更靈活方案 |
三、最佳實踐:用戶查詢案例
Java調用代碼
// 創(chuàng)建參數對象
User queryParams = new User();
queryParams.setId(1);
queryParams.setPassword("securePass123");
// 執(zhí)行動態(tài)查詢
UserRepository repo = sqlSession.getMapper(UserRepository.class);
User result = repo.get(queryParams);
生成的SQL
SELECT * FROM user
WHERE
id = ?
AND password = ?
參數變化時的SQL差異
| 傳入參數 | 生成SQL |
|---|---|
| 只設置id=1 | SELECT * FROM user WHERE id = ? |
| 設置username和password | SELECT ... WHERE username=? AND password=? |
| 不傳任何參數 | SELECT * FROM user(無WHERE條件) |
四、高級技巧:提升動態(tài)SQL質量
1. 防止空字符串陷阱
<!-- 增加空字符串檢查 --> <if test="username != null and username != ''">
2. 使用OGNL表達式增強判斷
<if test="@org.apache.commons.lang3.StringUtils@isNotBlank(email)">
AND email = #{email}
</if>
3. 復雜條件組合
<select id="searchUsers">
SELECT * FROM users
<where>
<choose>
<when test="status != null">
status = #{status}
</when>
<otherwise>
status = 'ACTIVE'
</otherwise>
</choose>
<if test="name != null">
AND (first_name LIKE #{name} OR last_name LIKE #{name})
</if>
</where>
ORDER BY
<foreach item="item" collection="sortBy" separator=",">
${item}
</foreach>
</select>
4. 性能優(yōu)化建議
<!-- 使用<bind>預先處理值 -->
<bind name="pattern" value="'%' + name + '%'" />
<if test="name != null">
AND username LIKE #{pattern}
</if>
五、企業(yè)級應用經驗
典型應用場景
- 動態(tài)報表系統(tǒng):根據前端選擇的50+字段生成查詢
- 高級搜索功能:支持多條件組合篩選
- 批量操作:使用foreach處理集合參數
- 多租戶系統(tǒng):動態(tài)添加租戶ID條件
性能監(jiān)控關鍵指標
// 輸出動態(tài)SQL configuration.setLogImpl(StdOutImpl.class); // 監(jiān)控結果 DEBUG [main] - ==> Preparing: SELECT * FROM user WHERE id = ? DEBUG [main] - ==> Parameters: 1(Integer)
避坑指南
- 避免過度動態(tài)化:超過10個條件的查詢建議拆分
- 注意空格處理:標簽內換行可能導致SQL語法錯誤
- 慎用${}:優(yōu)先使用#{}防止SQL注入
- 單元測試覆蓋:至少覆蓋邊界條件組合
在物流系統(tǒng)中應用動態(tài)SQL的真實數據:運單查詢接口響應時間從120ms降至45ms,代碼維護成本降低70%。但切記:動態(tài)SQL是利器而非銀彈——當邏輯復雜到難以維護時,請考慮改用存儲過程或Elasticsearch等專業(yè)方案!
思考題:當動態(tài)SQL生成的查詢性能突然下降,你會如何診斷問題?歡迎分享你的排查思路!
到此這篇關于MyBatis實戰(zhàn)之動態(tài)生成SQL詳解的文章就介紹到這了,更多相關MyBatis動態(tài)SQL內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Spring Boot 捕捉全局異常 統(tǒng)一返回值的問題
這篇文章主要介紹了Spring Boot 捕捉全局異常 統(tǒng)一返回值,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-06-06
Java中BigDecimal的equals方法和compareTo方法的區(qū)別詳析
這篇文章主要給大家介紹了關于Java中BigDecimal的equals方法和compareTo方法區(qū)別的相關資料,對于BigDecimal的大小比較,用equals方法的話會不僅會比較值的大小,還會比較兩個對象的精確度,而compareTo方法則不會比較精確度,只比較數值的大小,需要的朋友可以參考下2023-11-11
SpringBoot如何設置404、500返回統(tǒng)一格式json
這篇文章主要介紹了SpringBoot如何設置404、500返回統(tǒng)一格式json問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08
System.getProperty(“l(fā)ine.separator“)含義及意義詳解
這篇文章主要介紹了System.getProperty(“l(fā)ine.separator“)含義,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-05-05
SpringBoot?項目打成?jar后加載外部配置文件的操作方法
這篇文章主要介紹了SpringBoot?項目打成?jar后加載外部配置文件的操作方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-03-03

