Mybatis中# 和 $ 的使用详解

【1】#和$使用对比

① #和$详解

在JDBC中,主要使用的是两种语句:

  • 一种是支持参数化和预编译的PrepareStatement,能够支持原生的Sql,也支持设置占位符的方式,参数化输入的参数,防止Sql注入
  • 一种是支持原生Sql的Statement,有Sql注入的风险。

    #{key}:获取参数的值,预编译到SQL中。安全。
    ${key}:获取参数的值,拼接到SQL中。有SQL注入问题。ORDER BY ${name}

在使用Mybatis进行开发过程中,隐藏了底层具体使用哪一种语句的细节,我们通过使用#和$告诉Mybatis,我们实际上进行的是怎么样的操作,需要对语句进行参数化还是说直接保持原生状态就好。

在Mybatis的mapper.xml中经常看到这两个符号,其中 # 频率最高。

如下:

<insert id="insertUSer" parameterType="User" >
	insert into c_user (name,age)
	values(#{name},#{age})
</insert>

或者如下:

//...
<if test="SORTNAME !=null and SORTNAME !='' ">
	order by ${SORTNAME}
	<if test="SORTORDER !=null and SORTORDER !='' ">
		${SORTORDER} 
	</if>
</if>
limit ${(page-1)*pagesize},${pagesize}

下面说明一下二者的用法与区别

默认情况下,使用 #{ } 格式的语法会导致 MyBatis 创建预处理语句属性并以它为背景设置安全的值(比如 )。

这样做很安全,很迅速,也是首选的做法 !

但有时你只是想直接在SQL语句中插入一个不改变的字符串。比如,像 ORDER BY , 你可以这样来使用:

ORDER BY ${columnName}

这里 MyBatis 不会修改或转义字符串。如果sql语句中使用动态的表或者列,请使用$符号

接受从用户输出的内容并提供给语句中不变的字符串,这样做是不安全的,这会导致潜在的SQL注入攻击。因此你不应该允许用户输入这些字段,或者通常自行转义并检查。


② 使用#或者说PrepareStatement如何防止SQL攻击?

比如说根据学生姓名查学生信息,会传入一个name的参数,假设学生姓名是方方,那么Sql就是:

SELECT id,name,age FROM student WHERE name = '方方';

在没有做防Sql注入的时候,我们的Sql语句可能是这么写的

<select id="fetchStudentByName" parameterType="String" resultType="entity.StudentEntity"> 
SELECT id,name,age FROM student WHERE name = '${value}' 
</select>

但如果我们对传入的姓名参数做一些更改,比如改成anything' OR 'x'='x,那么拼接而成的Sql就变成了:

SELECT id,name,age FROM student WHERE name = 'anything' OR 'x'='x'

库里面所有的学生信息都被拉了出来,是不是很可怕。原因就是传入的anything’ OR ‘x’='x和原有的单引号,正好组成了 ‘anything’ OR ‘x’='x’,而OR后面恒等于1,所以等于对这个库执行了查所有的操作。

防范Sql注入的话,就是要把整个anything’ OR ‘x’='x中的单引号作为参数的一部分,而不是和Sql中的单引号进行拼接

使用了#即可在Mybatis中对参数进行转义:

<select id="fetchStudentByName" parameterType="String" resultType="entity.StudentEntity"> 
SELECT id,name,age FROM student WHERE name = #{name} 
</select>

我们看一下发送到数据库端的Sql语句长什么样子。

SELECT id,name,age FROM student WHERE name = 'anything\' OR \'x\'=\'x'

从上述代码中我们可以看到参数中的所有单引号统统被转义了,这都是JDBC中PrepareStatement的功劳。如果在数据库服务端开启了预编译,则是服务端来做了这件事情。

【2】使用动态表或列

有时候会不可避免使用动态表或者列进行业务处理。下面学习几种动态表/列的使用方式(其本质核心是使用$获取表名或列名):

① 使用预编译

即,默认值。

<select id="hisNumber" parameterType="hashmap" resultType="hashmap"  >
     select number from ${oldTableName} 
<!--这里使用"$"!!!-->
where name=#{name} and date = #{date}
<!--这里使用"#"-->
<select>

预编译,即首先会生成select number from ? where name=? and date=? 这样使用”?”作为占位符的语句,然后进行参数解析。

② 使用非预编译

<select id="hisNumber" parameterType="hashmap" resultType="hashmap" statementType="STATEMENT" >
     select number from ${oldTableName} 
<!--这里使用"$"!!!-->
where name='${name,jdbcType=VARCHAR}' and date = '${date,jdbcType=TIMESTAMP}'
<select>

注意后面name和date的取值,使用了'${name}' 格式,这样会对参数进行数据类型转换,有助于mysql查询时提升性能。

仍旧使用非预编译

<select id="hisNumber" parameterType="hashmap" resultType="hashmap" statementType="STATEMENT" >
     select number from ${oldTableName} 
<!--这里使用"$"!!!-->
where name=${name} and date = ${date}
<select>

注意后面name和date的取值,使用了${name} 格式,将会直接取参数值,不进行数据类型转换。当参数为数值类型且格式如“00124”时,将会出现数据错读(会将0124、124等都读出来)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流烟默

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值