题目
MyBatis中#{}和${}的区别是什么?
信息
- 类型:问答
- 难度:⭐
考点
参数占位符,SQL注入,动态SQL
快速回答
在MyBatis中,#{}和${}都是参数占位符,但有以下核心区别:
- #{}:使用预编译处理,能防止SQL注入,会自动添加引号处理字符串
- ${}:直接拼接SQL字符串,有SQL注入风险,需手动处理引号
- 使用场景:#{}适用于值传递(如WHERE条件),${}适用于动态表名/列名
1. 核心原理
#{} 工作原理:
- MyBatis会将其转换为JDBC的PreparedStatement参数占位符(?)
- 执行时通过setXxx()方法安全设置参数值
- 示例:
SELECT * FROM user WHERE name = #{name}→ 编译为SELECT * FROM user WHERE name = ?
${} 工作原理:
- 直接进行字符串替换(文本替换)
- 生成原生SQL语句
- 示例:
SELECT * FROM ${tableName}→ 替换为SELECT * FROM orders
2. 代码示例对比
<!-- 安全用法 -->
<select id="findUser" resultType="User">
SELECT * FROM user WHERE name = #{name}
</select>
<!-- 风险用法(需手动过滤) -->
<select id="dynamicTable" resultType="map">
SELECT * FROM ${tableName} WHERE status = 1
</select>3. 最佳实践
- 优先使用#{}: 适用于所有值传递场景(WHERE条件、INSERT值等)
- 谨慎使用${}: 仅用于动态表名/列名等非值场景
- 防注入措施: 使用${}时必须对传入参数白名单校验:
// 示例:表名白名单校验 private static final Set<String> SAFE_TABLES = Set.of("users","orders"); public List<Map> queryTable(String tableName) { if(!SAFE_TABLES.contains(tableName)) { throw new IllegalArgumentException("Invalid table name"); } return sqlSession.selectList("dynamicTable", tableName); }
4. 常见错误
- 混淆使用场景: 在WHERE条件中使用${}导致SQL注入风险
- 引号处理错误: ${}用于字符串时忘记加单引号:
-- 错误写法(导致语法错误) WHERE name = ${name} -- 正确写法 WHERE name = '${name}' - 未做参数校验: 动态表名未校验直接拼接
5. 扩展知识
- 预编译优势: #{}生成的预编译SQL可被数据库缓存,提高执行效率
- Like查询处理: 模糊查询应使用CONCAT函数:
WHERE name LIKE CONCAT('%', #{keyword}, '%') - OGNL表达式: #{}内可调用Java方法(如
#{name.toUpperCase()})