MyBatis中使用$和#所遇到的問題及解決辦法
在上篇文章給大家介紹了Mybatis中#{}和${}傳參的區(qū)別及#和$的區(qū)別小結(jié),如果大家有需要可以參考下。
$和#簡單說明:
#相當(dāng)于對數(shù)據(jù) 加上 雙引號,$相當(dāng)于直接顯示數(shù)據(jù)。
一、總結(jié)
mybatis中使用sqlMap進行sql查詢時,經(jīng)常需要動態(tài)傳遞參數(shù)。動態(tài)SQL是mybatis的強大特性之一,也是它優(yōu)于其他ORM框架的一個重要原因。mybatis在對sql語句進行預(yù)編譯之前,會對sql進行動態(tài)解析,解析為一個BoundSql對象,也是在此處對動態(tài)SQL進行處理的。在動態(tài) SQL 解析階段,#{ }和${ }會有不同的表現(xiàn),#{ }解析為一個JDBC預(yù)編譯語句(prepared statement)的參數(shù)標(biāo)記符。
一個 #{ } 被解析為一個參數(shù)占位符 ? 。${ } 僅僅為一個純碎的 string 替換,在動態(tài) SQL 解析階段將會進行變量替換。
二、Bug描述
前端傳入?yún)?shù):
skip:0
take:10
ruleName:A,B,C
業(yè)務(wù)層處理:
package SQL; /** * 將前端多選參數(shù)轉(zhuǎn)義為SQL語句內(nèi)容 */ public class SQLUtil { private final static String REPLACECHAR_COMMA = ","; private final static String REPLACECHAR_SEMICOLON = ";"; public static void main(String[] args) { String s1 = "A,B,C"; String s2 = "A B C"; System.out.println("逗號分隔:" + formatInStr(s1)); System.out.println("空格分隔:" + formatInStr(s2)); } private static String formatInStr(String queryStr) { return queryInStr(sliptQueryStr(queryStr)); } private static String[] sliptQueryStr(String queryStr) { if (null == queryStr || "".equals(queryStr.trim())) return null; queryStr = queryStr.replaceAll(SQLUtil.REPLACECHAR_COMMA, " ").replaceAll(REPLACECHAR_SEMICOLON, " "); return queryStr.split("\\s+"); } private static String queryInStr(String[] queryStrs) { if (null == queryStrs || 0 == queryStrs.length) return null; StringBuffer buf = new StringBuffer(); for (int i = 0; i < queryStrs.length; i++) { if (i != 0) buf.append(","); buf.append("'").append(queryStrs[i]).append("'"); } return buf.toString(); } }
Mapper層處理:
//錯誤的處理 <if test="ruleName != null and ruleName != ''"> AND a.rule_name IN (#{ruleName}) </if> //正確的處理 <if test="ruleName != null and ruleName != ''"> AND a.rule_name IN (${ruleName}) </if>
日志描述:
[DEBUG] [2016-08-02 17:42:42.226] [qtp1457334982-157] java.sql.Connection - ==> Preparing: SELECT a.id, a.is_valid, a.rule_lable, a.rule_name, a.type, b.sp_id, b.sp_name, a.rule_content, c.user_name, a.gmt_modified, a.ordering FROM idc_logistics_assign_rules a LEFT JOIN app_user c on c.work_no=a.modifier and c.is_deleted='n', idc_sp_info b WHERE a.is_deleted = 'n' AND b.is_deleted = 'n' AND a.sp_id = b.sp_id AND a.rule_name IN (?) ORDER BY ordering asc limit ?, ? [DEBUG] [2016-08-02 17:42:42.226] [qtp1457334982-157] java.sql.PreparedStatement - ==> Parameters: 'A','B'(String), 0(Integer), 10(Integer)
結(jié)果分析:mapper層對sql有預(yù)編譯處理,對于#有占位符?,但是對于$會直接替換。
PS:MyBatis排序時使用order by 動態(tài)參數(shù)時需要注意,用$而不是#
字符串替換
默認情況下,使用#{}格式的語法會導(dǎo)致MyBatis創(chuàng)建預(yù)處理語句屬性并以它為背景設(shè)置安全的值(比如?)。這樣做很安全,很迅速也是首選做法,有時你只是想直接在SQL語句中插入一個不改變的字符串。比如,像ORDER BY,你可以這樣來使用:
ORDER BY ${columnName}
這里MyBatis不會修改或轉(zhuǎn)義字符串。
重要:接受從用戶輸出的內(nèi)容并提供給語句中不變的字符串,這樣做是不安全的。這會導(dǎo)致潛在的SQL注入攻擊,因此你不應(yīng)該允許用戶輸入這些字段,或者通常自行轉(zhuǎn)義并檢查。
相關(guān)文章
JAVA簡單工廠模式(從現(xiàn)實生活角度理解代碼原理)
本文主要介紹了JAVA簡單工廠模式(從現(xiàn)實生活角度理解代碼原理)的相關(guān)知識。具有很好的參考價值。下面跟著小編一起來看下吧2017-03-03Flyway詳解及Springboot集成Flyway的詳細教程
Flayway是一款數(shù)據(jù)庫版本控制管理工具,,支持?jǐn)?shù)據(jù)庫版本自動升級,Migrations可以寫成sql腳本,也可以寫在java代碼里。這篇文章主要介紹了Flyway詳解及Springboot集成Flyway的詳細教程的相關(guān)資料,需要的朋友可以參考下2020-07-07Java中的HashMap和Hashtable區(qū)別解析
這篇文章主要介紹了Java中的HashMap和Hashtable區(qū)別解析,HashMap和Hashtable都實現(xiàn)了Map接口,但決定用哪一個之前先要弄清楚它們之間的區(qū)別,主要的區(qū)別有線程安全性、同步和速度,需要的朋友可以參考下2023-11-11Java 添加、刪除、格式化Word中的圖片步驟詳解( 基于Spire.Cloud.SDK for Java )
這篇文章主要介紹了Java 添加、刪除、格式化Word中的圖片( 基于Spire.Cloud.SDK for Java ),本文分步驟通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08利用Java將2019拆分成三個素數(shù)平方和的方法實例
這篇文章主要給大家介紹了關(guān)于利用Java將2019拆分成三個素數(shù)平方和的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05Spring實戰(zhàn)之使用靜態(tài)工廠方法創(chuàng)建Bean操作示例
這篇文章主要介紹了Spring實戰(zhàn)之使用靜態(tài)工廠方法創(chuàng)建Bean操作,結(jié)合實例形式分析了靜態(tài)工廠方法創(chuàng)建Bean的相關(guān)實現(xiàn)步驟與操作注意事項,需要的朋友可以參考下2019-11-11