Mybatis中# 和 $ 的使用详解

简介: 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等都读出来)。


目录
相关文章
|
存储 中间件 关系型数据库
数据库切片大对决:ShardingSphere与Mycat技术解析
数据库切片大对决:ShardingSphere与Mycat技术解析
1367 0
|
Java API Maven
敏感数据的保护伞——SpringBoot Jasypt加密库的使用
我们经常会在yml配置文件中存放一些敏感数据,比如数据库的用户名、密码,第三方应用的秘钥等等。这些信息直接以明文形式展示在文件中,无疑是存在较大的安全隐患的,所以今天这篇文章,我会借助jasypt实现yml文件中敏感信息的加密处理。
4897 1
敏感数据的保护伞——SpringBoot Jasypt加密库的使用
|
11月前
|
存储 Java
Bitmap位图(Java实现)
本文介绍了使用Java实现一个简单的Bitmap,通过自定义byte数组存储数据,提供put和exist方法分别用于插入数据和查询数据是否存在。Bitmap利用位操作高效地管理大量布尔值,适用于空间优化的场景。代码中详细解释了位图的核心原理、方法实现及边界检查。后续计划探讨位图在海量数据去重中的应用及JDK BitSet源码分析。
755 7
|
SQL 关系型数据库 数据库
学习分布式事务Seata看这一篇就够了,建议收藏
学习分布式事务Seata看这一篇就够了,建议收藏
19412 2
|
7月前
|
人工智能 Java API
MCP协议重大升级,Spring AI Alibaba联合Higress发布业界首个Streamable HTTP实现方案
本文由Spring AI Alibaba Contributor刘军、张宇撰写,探讨MCP官方引入的全新Streamable HTTP传输层对原有HTTP+SSE机制的重大改进。文章解析Streamable HTTP的设计思想与技术细节,并介绍Spring AI Alibaba开源框架提供的Java实现,包含无状态服务器模式、流式进度反馈模式等多种场景的应用示例。同时,文章还展示了Spring AI Alibaba + Higress的完整可运行示例,分析当前实现限制及未来优化方向,为开发者提供参考。
|
8月前
|
负载均衡 Java Nacos
Spring Cloud五大组件
Spring Cloud五大组件
|
缓存 Java 开发工具
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
三级缓存是Spring框架里,一个经典的技术点,它很好地解决了循环依赖的问题,也是很多面试中会被问到的问题,本文从源码入手,详细剖析Spring三级缓存的来龙去脉。
1301 24
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
|
消息中间件 存储 Kafka
RocketMQ 工作原理图解,看这篇就够了!
本文详细解析了 RocketMQ 的核心架构、消息领域模型、关键特性和应用场景,帮助深入理解消息中间件的工作原理。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
RocketMQ 工作原理图解,看这篇就够了!
|
11月前
|
消息中间件 存储 运维
2024最全RabbitMQ集群方案汇总
本文梳理了RabbitMQ集群的几种方案,主要包括普通集群、镜像集群(高可用)、Quorum队列(仲裁队列)、Streams集群模式(高可用+负载均衡)和插件方式。重点介绍了每种方案的特点、优缺点及适用场景。搭建步骤包括安装Erlang和RabbitMQ、配置集群节点、修改hosts文件、配置Erlang Cookie、启动独立节点并创建集群,以及配置镜像队列以提高可用性和容错性。推荐使用Quorum队列与Streams模式,其中Quorum队列适合高可用集群,Streams模式则同时支持高可用和负载均衡。此外,还有Shovel和Federation插件可用于特定场景下的集群搭建。
2395 2
|
消息中间件 存储 缓存
消息中间件系列教程(06) -RabbitMQ -五种队列形式
消息中间件系列教程(06) -RabbitMQ -五种队列形式
1884 1