MyBatis 动态参数时需要注意,用$而不是#

本文介绍MyBatis中的字符串替换方法,包括如何使用#{}

1、字符串替换
默认情况下,使用#{}格式的语法会导致MyBatis创建预处理语句属性并以它为背景设置安全的值(比如?)。这样做很安全,很迅速也是首选做法,有时你只是想直接在SQL语句中插入一个不改变的字符串。比如,像ORDER BY,可以这样来使用:
ORDER BY ${columnName}
这里MyBatis不会修改或转义字符串。
重要:接受从用户输出的内容并提供给语句中不变的字符串,这样做是不安全的。这会导致潜在的SQL注入攻击,因此你不应该允许用户输入这些字段,或者通常自行转义并检查。


2、特殊符号处理

在Mybatis的配置文件中经常会用到<、>等符号,在XML的配置文件中会存在问题。可以通过加入<![CDATA[ ....]]>让在标签中的特殊符号不被处理。如:

<select id="selectNextArticle" resultType="Article">
        SELECT 
            <include refid="cmsArticleColumns"/>
        FROM cms_article a
        <include refid="cmsArticleJoins"/>
        <![CDATA[
        WHERE a.article_id > #{articleId} ORDER BY id DESC LIMIT 1
        ]]>
    </select>

<think>我们正在讨论MyBatis中的#{}和${}占位符的区别和联系。根据引用内容,我们可以总结如下: 1. **#{}**:这是预编译处理方式。MyBatis会使用`?`作为占位符,然后通过PreparedStatement来设置参数,这样可以有效防止SQL注入攻击。同,由于数据库会对预编译的SQL进行缓存,因此可以提高性能。适用于大多数情况,尤其是传入普通参数值(如字符串、数字等)。 2. **${}**:这是字符串替换方式。MyBatis会直接将参数值替换到SQL语句中,相当于字符串拼接。这种方式存在SQL注入的风险,因为如果参数值中包含SQL片段,就会被解释执行。但在某些场景下是必需的,比如动态传入表名、列名或者排序字段(order by子句)等。 **区别总结**: - **安全性**:`#{}`能防止SQL注入,`${}`不能。 - **性能**:`#{}`通常有更好的性能(因为预编译和缓存),而`${}`每次都需要重新编译SQL(如果动态内容变化)。 - **使用场景**:`#{}`用于参数值传递;`${}`用于动态SQL片段(如表名、列名等)。 **联系**: 两者都是MyBatis中用于动态传递参数的语法,都是实现动态SQL的一部分。 **代码示例**(参考引用[4]): ```xml <!-- 使用${},存在SQL注入风险 --> <select id="login" resultType="com.example.demo.model.UserInfo"> select * from userinfo where username=&#39;${username}&#39; and password=&#39;${password}&#39; </select> ``` 上述代码中,如果`username`传入`&#39; or 1=1 -- `,那么整个SQL就会变成: ```sql select * from userinfo where username=&#39;&#39; or 1=1 -- &#39; and password=&#39;...&#39; ``` 这样就会绕过密码验证,这就是SQL注入。 而使用`#{}`: ```xml <select id="login" resultType="com.example.demo.model.UserInfo"> select * from userinfo where username=#{username} and password=#{password} </select> ``` MyBatis会将其预编译为: ```sql select * from userinfo where username=? and password=? ``` 然后通过PreparedStatement设置参数,即使传入恶意参数,也会被当作字符串处理,不会改变SQL结构。 **注意事项**: - 动态表名、列名等必须使用`${}`,例如:`select * from ${tableName}`。 - 在`order by`后面动态指定排序列,也必须使用`${}`,例如:`order by ${columnName}`。 **最佳实践**: - 尽可能使用`#{}`,避免SQL注入风险。 - 在必须使用`${}`,要确保参数值的安全性,比如对传入的表名、列名进行白名单校验等。 希望以上解析能帮助您理解MyBatis#{}和${}的区别与联系。 §§相关问题§§ 1. 在MyBatis中如何安全地使用${}防止SQL注入? 2. MyBatis预编译处理的具体过程是怎样的? 3. 除了表名和排序字段,还有哪些场景必须使用${}?</think>### MyBatis中`#{}`和`${}`占位符的区别与联系 #### 核心区别 | 特性 | `#{}` (预编译占位符) | `${}` (字符串替换符) | |--------------|-----------------------------------|-----------------------------------| | **处理方式** | 生成预编译SQL(`?`占位) | 直接替换为参数值 | | **安全性** | ✅ 防止SQL注入 | ❌ 存在SQL注入风险[^2][^3] | | **性能** | ✅ 支持预编译缓存,效率较高[^1] | ❌ 每次重新编译SQL | | **适用场景** | 普通参数值(字符串/数字) | 动态SQL片段(表名/列名/排序)[^2] | | **输出示例** | `where username = ?` | `where username = &#39;admin&#39;` | #### 联系与共性 1. **动态SQL基础**:两者都是MyBatis实现动态SQL的核心语法[^3] 2. **参数传递**:都用于将Java变量值传递到SQL语句中 3. **嵌套使用**:可在同一SQL语句中混合使用(需注意安全风险) #### 代码示例对比 ```xml <!-- 安全示例:使用#{}防止注入 --> <select id="safeLogin"> SELECT * FROM users WHERE username = #{username} AND password = #{password} <!-- 预编译处理 --> </select> <!-- 风险示例:${}导致SQL注入漏洞[^4] --> <select id="unsafeLogin"> SELECT * FROM users WHERE username = &#39;${username}&#39; AND password = &#39;${password}&#39; <!-- 直接字符串拼接 --> </select> ``` **攻击模拟**:若传入`username = &#39; OR 1=1 --`,`${}`生成的SQL将变成: ```sql SELECT * FROM users WHERE username = &#39;&#39; OR 1=1 --&#39; AND ... ``` 这将返回所有用户数据,导致安全漏洞[^3]。 #### 最佳实践 1. **优先使用#{}**:处理用户输入、普通参数值[^1][^2] 2. **限制使用${}**:仅用于动态对象(表名/列名),且需白名单校验 ```xml <!-- 正确使用${}的场景 --> <select id="getData"> SELECT * FROM ${tableName} <!-- 动态表名 --> ORDER BY ${sortColumn} <!-- 动态排序 --> </select> ``` 3. **混合使用隔离风险**: ```xml <select id="dynamicQuery"> SELECT * FROM ${tableName} WHERE category = #{category} <!-- 安全参数用#{} --> </select> ``` > 关键结论:**能用`#{}`绝不用`${}`**,动态对象必须使用`${}`应进行输入验证[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

xiejava1018

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

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

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

打赏作者

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

抵扣说明:

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

余额充值