MyBatis中使用$和#所遇到的问题及解决办法
在上篇文章给大家介绍了Mybatis中#{}和${}传参的区别及#和$的区别小结,如果大家有需要可以参考下。
$和#简单说明:
#相当于对数据加上双引号,$相当于直接显示数据。
一、总结
mybatis中使用sqlMap进行sql查询时,经常需要动态传递参数。动态SQL是mybatis的强大特性之一,也是它优于其他ORM框架的一个重要原因。mybatis在对sql语句进行预编译之前,会对sql进行动态解析,解析为一个BoundSql对象,也是在此处对动态SQL进行处理的。在动态SQL解析阶段,#{}和${}会有不同的表现,#{}解析为一个JDBC预编译语句(preparedstatement)的参数标记符。
一个#{}被解析为一个参数占位符?。${}仅仅为一个纯碎的string替换,在动态SQL解析阶段将会进行变量替换。
二、Bug描述
前端传入参数:
skip:0
take:10
ruleName:A,B,C
业务层处理:
packageSQL; /** *将前端多选参数转义为SQL语句内容 */ publicclassSQLUtil{ privatefinalstaticStringREPLACECHAR_COMMA=","; privatefinalstaticStringREPLACECHAR_SEMICOLON=";"; publicstaticvoidmain(String[]args){ Strings1="A,B,C"; Strings2="ABC"; System.out.println("逗号分隔:"+formatInStr(s1)); System.out.println("空格分隔:"+formatInStr(s2)); } privatestaticStringformatInStr(StringqueryStr){ returnqueryInStr(sliptQueryStr(queryStr)); } privatestaticString[]sliptQueryStr(StringqueryStr){ if(null==queryStr||"".equals(queryStr.trim()))returnnull; queryStr=queryStr.replaceAll(SQLUtil.REPLACECHAR_COMMA,"").replaceAll(REPLACECHAR_SEMICOLON,""); returnqueryStr.split("\\s+"); } privatestaticStringqueryInStr(String[]queryStrs){ if(null==queryStrs||0==queryStrs.length)returnnull; StringBufferbuf=newStringBuffer(); for(inti=0;i<queryStrs.length;i++){ if(i!=0)buf.append(","); buf.append("'").append(queryStrs[i]).append("'"); } returnbuf.toString(); } }
Mapper层处理:
//错误的处理 <iftest="ruleName!=nullandruleName!=''"> ANDa.rule_nameIN(#{ruleName}) </if> //正确的处理 <iftest="ruleName!=nullandruleName!=''"> ANDa.rule_nameIN(${ruleName}) </if>
日志描述:
[DEBUG][2016-08-0217:42:42.226][qtp1457334982-157]java.sql.Connection-==>Preparing:SELECTa.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.orderingFROMidc_logistics_assign_rulesaLEFTJOINapp_userconc.work_no=a.modifierandc.is_deleted='n',idc_sp_infobWHEREa.is_deleted='n'ANDb.is_deleted='n'ANDa.sp_id=b.sp_idANDa.rule_nameIN(?)ORDERBYorderingasclimit?,? [DEBUG][2016-08-0217:42:42.226][qtp1457334982-157]java.sql.PreparedStatement-==>Parameters:'A','B'(String),0(Integer),10(Integer)
结果分析:mapper层对sql有预编译处理,对于#有占位符?,但是对于$会直接替换。
PS:MyBatis排序时使用orderby动态参数时需要注意,用$而不是#
字符串替换
默认情况下,使用#{}格式的语法会导致MyBatis创建预处理语句属性并以它为背景设置安全的值(比如?)。这样做很安全,很迅速也是首选做法,有时你只是想直接在SQL语句中插入一个不改变的字符串。比如,像ORDERBY,你可以这样来使用:
ORDERBY${columnName}
这里MyBatis不会修改或转义字符串。
重要:接受从用户输出的内容并提供给语句中不变的字符串,这样做是不安全的。这会导致潜在的SQL注入攻击,因此你不应该允许用户输入这些字段,或者通常自行转义并检查。