处理数据访问参数的基础知识点,直接关系到 SQL 执行的安全性和规范性
一、#{}预编译参数绑定(推荐使用)
#{}是 MyBatis 参数引用的核心方式,其底层实现和核心特性是该知识点的重点:
底层实现
MyBatis 在解析
#{}时,会将其转换为 JDBC 中的PreparedStatement(预编译语句对象)的占位符?,后续参数值会通过setXxx()方法(如setInt()、setString())传入占位符,而不是直接嵌入 SQL 语句三大核心特性(推荐使用的原因)
- 防 SQL 注入(核心优势):由于参数值是作为独立数据传入预编译语句,而非 SQL 语句的组成部分,即使参数中包含恶意 SQL 片段(如
' or 1=1 --),也会被当作普通字符串处理,无法执行恶意逻辑,有效保障数据库安全 - 自动类型转换:MyBatis 会自动匹配 Java 方法参数类型与数据库表字段类型,完成数据类型的转换(如 Java 的
Integer转换为数据库的INT,String转换为VARCHAR),无需开发者手动处理类型兼容问题 - 兼容性强:支持各种复杂参数类型(基本数据类型、实体类、Map、注解指定参数名等),适配绝大多数业务场景
- 防 SQL 注入(核心优势):由于参数值是作为独立数据传入预编译语句,而非 SQL 语句的组成部分,即使参数中包含恶意 SQL 片段(如
二、${}字符串直接拼接(慎用)
这是与#{}对应的参数引用方式,也是该知识点的重要补充,需明确其特性和适用场景:
底层实现
MyBatis 解析
${}时,会直接将传入的参数值进行字符串拼接,嵌入到 SQL 语句的对应位置,底层对应 JDBC 的Statement(普通语句对象),无预编译处理核心特性(局限性为主)
- 无防 SQL 注入能力(核心风险):参数值直接拼接进 SQL,若参数包含恶意 SQL 片段,会被直接执行,存在严重的数据库安全隐患
- 无自动类型转换:仅做纯字符串拼接,需要开发者手动保证参数类型与数据库字段类型兼容,否则会抛出 SQL 执行异常
- 支持动态占位(唯一优势):可以动态指定 SQL 中的表名、列名、排序字段、查询条件关键字(如
ASC/DESC)等,这些场景#{}无法满足(因为预编译占位符?不能替代表名、列名等 SQL 关键字)
适用场景(仅限特殊需求)
仅用于无需考虑注入风险(如参数由系统内部生成,非用户输入)的动态 SQL 场景,示例:
<!-- 动态指定排序字段和排序方式,只能使用${} --> <select id="getUserList" resultType="com.example.entity.User"> SELECT * FROM user ORDER BY ${sortColumn} ${sortType} </select>三、核心价值与注意事项
核心价值
- 掌握该机制能保证 SQL 执行的安全性,避免核心的 SQL 注入漏洞
- 合理选择
#{}和${},既能满足常规业务需求,又能适配特殊动态 SQL 场景,提升开发的规范性和灵活性
必备注意事项
- 开发中优先使用
#{},这是 MyBatis 的推荐最佳实践,最大限度保障系统安全 - 使用
${}时必须做严格的参数校验和过滤,禁止直接使用用户输入的参数作为${}的取值,避免注入风险
- 开发中优先使用
总结
- 核心知识点:MyBatis 参数解析与引用机制(
#{}预编译绑定 vs${}字符串拼接) - 重点:
#{}的防注入、自动类型转换特性,以及${}的适用场景和风险 - 开发原则:优先用
#{},慎用${}并做好参数校验