Mybatis-删除操作

增删改查操作之 —— 删除

需求:根据ID删除用户信息

  • Mapper接口方法:

    根据id删除

@Delete("delete from user where id = #{id}")
public void deleteById(Integer id);

在Mybatis中,我们可以通过参数占位符号 #{ } 来占位,在调用deleteById方法时,传递的参数值,最终会替换占位符。

  • 编写单元测试方法进行测试

    在单元测试类中,增加如下测试方法.

@Test
public void testDeleteById(){
    userMapper.deleteById(36);
}

运行之后,我们发现,#{...} 占位符,其实最终被替换成了 ?占位符,生成的是预编译的SQL语句

  • DML语句执行完毕,是有返回值的,我们可以为Mapper接口方法定义返回值来接收,如下:

    根据id删除

@Delete("delete from user where id = #{id}")
public Integer deleteById(Integer id);
  • 一、Mybatis的提供的符号,有两个,一个是 #{...},另一个是** ${...}**,区别如下:

image

那在企业项目开发中,强烈建议使用 #{...}

这是 MyBatis 里一个非常经典、也非常重要的问题。
核心区别就一句话:

  • #{}预编译参数,占位符方式,安全
  • ${}:字符串直接拼接不安全

二、什么时候必须用 ${}?

通常只有:

场景 示例
动态表名 from ${tableName}
动态列名 order by ${column}
动态排序 ${sort}
limit 某些数据库 limit ${n}

三、SQL 预编译到底是怎么工作的?

这里最关键的一点是:

? 只能替代“值”,不能替代 SQL 结构

比如:

✅ 可以替代:

  • 字符串值
  • 数字值
  • 日期值

❌ 不能替代:

  • 表名
  • 列名
  • SQL关键字

所以:

? 只能放“值”的位置

不能放:

  • 列名
  • 表名
  • 排序规则

因为数据库编译 SQL 时:

必须先知道操作哪张表、哪一列

四、一个非常形象的例子

想象 SQL 是:

我要按 ___ 排序

数据库需要提前知道:

  • 按年龄?
  • 按姓名?
  • 按时间?

因为:

排序方案不同

执行计划都不同。

所以:

order by ?

数据库会懵:

你到底让我按什么排?

五、再看 where

where age = ?

数据库完全没问题:

我知道是 age 列

只是:

具体等于多少以后再说

这就合法。

六、为什么 ${} 会导致 SQL 注入
一、假设有个登录功能

用户输入:

  • 用户名:admin
  • 密码:123456

Java:

String username = inputUsername;
String password = inputPassword;

二、错误写法(${} 本质)

很多初学者会这样拼 SQL:

String sql =
    "select * from user where username = '"
    + username +
    "' and password = '"
    + password + "'";

三、正常情况下

用户输入:

  • username = admin
  • password = 123456

SQL:

select * from user
where username = 'admin'
and password = '123456'

没问题。

四、黑客来了(重点)

黑客输入:

username = admin' --
password = 随便写

你拼出来的 SQL:

select * from user
where username = 'admin' --'
and password = 'xxxx'

五、问题出现了

在 SQL 里:

--表示:注释

后面全部失效。

于是 SQL 实际变成:

select * from user
where username = 'admin'

密码判断:and password = 'xxxx' 被注释掉了

六、结果

黑客:

不需要密码

直接登录成功。

这就是:

SQL 注入

七、为什么 #{} 不会这样?

底层:

where username = ?

数据库会把用户输入:

admin' --

当成:

普通字符串

而不是 SQL 代码

相当于:

where username = " admin' --"

数据库会认为:

你是在查用户名叫:
admin' --
的人

SQL结构完全没变。

因此:

注入失败

为什么黑客要在admin后面多加一个'

因为SQL的语法是以单引号作为字符串的标识的,当你写where username = ?的时候,实际sql识别到的是'?',然后把''里面的内容传递给username,而黑客为了使'提前闭合会在字符串中添加一个'符号,这样如果直接拼接,就会形成一个SQL语句 where username = 'admin' ,加入'后面还跟着--,那么后面的语句就会被注释掉,这就是SQL注入的不安全,而#{},并不会直接把字符串拼接上去,而是通过预编译之后,把admin'--作为字符串传进去,相当于sql识别到的是
:' admin'-- ',虽然前面两个单引号看似闭合,但由于并不是直接拼接,换句话说sql语句已经通过预编译了,它指导你是准备传一个数据值进来,因此它已经把两个''闭合了,你传进来的值只是作为字符串而不会改变SQL语句,所以这就是为什么我们在传数据值的时候使用#{}而不是\({},就是为了规避SQL注入不安全的问题,此外,通过上面的解释,我们也能发现,在某些情况下是不能使用#{}的,比如说 order by这种SQL语句后面是不能接参数值的,根据预编译处理的逻辑,SQL预先通过编译然后才进行参数的处理,但假如你的SQL语句都无法通过编译,那后续自然就无法再进行参数注入,那为什么说这样无法通过编译呢? 因为SQL是需要根据字段进行操作的,在前面使用#{}的时候,SQL已经指导它处理的是username字段,所以可以通过预编译,但是order by#{}并没有字段信息,自然无法通过预编译,在这种情况下是必须使用\){}进行拼接的,从以上我们可以总结出#{}通常是作为数据值的占位符,而${}则作为字段,SQL关键字的占位符,这就是二者的区别与适用场景。

posted @ 2026-05-26 20:07  ZealousMclaren  阅读(10)  评论(0)    收藏  举报