MyBatis 里 $ 和
这道题本质是 预编译 vs 字符串拼接,面试官想听你会不会 SQL 注入。结合动态表名、排序字段说明
$不是完全不能用。
题目
MyBatis 中 $ 和 # 有什么区别?
参考答案(口述要点)
#{}:预编译占位符?,参数带引号、防注入,值 都用它${}:字符串原样替换到 SQL,表名、列名、ORDER BY 等没法预编译时用,必须白名单校验- 生产条件值一律
#,${}慎用
详细解析
#{} 过程
<select id="findByName" resultType="User">
SELECT * FROM user WHERE name = #{name}
</select>发送给数据库类似:SELECT * FROM user WHERE name = ?,参数 name 单独绑定。
${} 过程
<select id="listByTable" resultType="Map">
SELECT * FROM ${tableName} WHERE status = #{status}
</select>${tableName} 直接拼进 SQL;#{status} 仍是 ?。若 tableName 来自前端且未校验,等于 SQL 注入入口。
代码与实践
1. 正常查询(只用 #)
<select id="selectById">
SELECT * FROM orders WHERE id = #{id}
</select>2. 动态排序(${} + 白名单)
private static final Set<String> ALLOW_SORT = Set.of("create_time", "amount");
public List<Order> list(String sortColumn) {
if (!ALLOW_SORT.contains(sortColumn)) {
sortColumn = "create_time";
}
Map<String, Object> param = Map.of("sortColumn", sortColumn);
return orderMapper.listOrderBy(param);
}<select id="listOrderBy">
SELECT * FROM orders ORDER BY ${sortColumn} DESC
</select>3. 项目踩坑
日志里打印完整 SQL 时,# 会显示 ?,要用 p6spy 或 MyBatis 日志插件看真实参数;别把 ${} 用在用户输入的 搜索关键字 上。
写在最后
一句话:**# 传值,$ 拼字;值用 #,结构用 $且必须校验。** 面试官追问注入,举name = ${name}被拼成or 1=1` 的例子即可。
创作不易,转载请注明出处和作者。
