Mybatis的mapper文件中#和$的区别示例解析

 更新时间:2025年12月08日 11:21:03   作者:Tan_Ying_Y  
MyBatis的mapper文件中,#{}和${}是两种参数占位符,核心差异在于参数解析方式、SQL 注入风险、适用场景,以下从底层原理、使用场景、示例等维度详细解析,本文介绍Mybatis的mapper文件中#和$的区别,感兴趣的朋友跟随小编一起看看吧

MyBatis 中 mapper 文件里 #{} 与 ${} 的核心区别

MyBatis 的 mapper 文件中,#{}${}是两种参数占位符,核心差异在于参数解析方式、SQL 注入风险、适用场景,以下从底层原理、使用场景、示例等维度详细解析:

一、核心区别对比表

特性#{}${}
解析方式预编译处理:MyBatis 将#{}替换为?,通过 PreparedStatement 设置参数(setXxx()字符串替换:直接将参数拼接到 SQL 语句中,无预编译
SQL 注入风险安全(参数会被自动转义)危险(直接拼接,易触发 SQL 注入)
参数类型处理自动识别参数类型,自动加引号(如字符串参数拼接后为'value'不处理类型,直接拼接(字符串需手动加引号)
适用场景常规参数传递(列值、条件值)动态表名、列名、排序字段等 SQL 语法部分
性能预编译可复用执行计划,性能高每次拼接生成新 SQL,性能低

二、底层原理与示例

1.#{}:预编译占位符(推荐)

原理

MyBatis 解析#{}时,会将其替换为 JDBC 的?占位符,生成PreparedStatement,并通过setInt()/setString()等方法设置参数,参数会被自动转义(如特殊字符'会被处理为\'),从根本上避免 SQL 注入。

示例

Mapper 文件

<select id="getUserById" resultType="User">
    SELECT id, name, age FROM user WHERE id = #{id}
</select>

实际执行的 SQL(参数 id=1)

-- MyBatis生成PreparedStatement,参数通过setInt(1, 1)设置
SELECT id, name, age FROM user WHERE id = ?

字符串参数示例(name="张三")

<select id="getUserByName" resultType="User">
    SELECT id, name, age FROM user WHERE name = #{name}
</select>

实际执行:参数会被自动加引号,等价于WHERE name = '张三',若参数含特殊字符(如张三' OR 1=1),会被转义为'张三\' OR 1=1',避免注入。

2.${}:字符串拼接(慎用)

原理

MyBatis 直接将${}替换为参数的原始字符串,无任何转义处理,相当于 “硬拼接” SQL,因此存在严重的 SQL 注入风险,但可用于动态指定 SQL 语法的部分(如表名、列名)。

示例

动态表名(按年月分表)

<select id="getUserByMonth" resultType="User">
    SELECT id, name FROM user_${month} WHERE id = #{id}
</select>

参数 month="202512",id=1 时,实际执行 SQL

SELECT id, name FROM user_202512 WHERE id = ?

风险示例(SQL 注入):若使用${}接收用户输入的排序字段:

<select id="getUserList" resultType="User">
    SELECT id, name FROM user ORDER BY ${sortField}
</select>

当用户传入sortField="id; DROP TABLE user;"时,拼接后的 SQL 为:

SELECT id, name FROM user ORDER BY id; DROP TABLE user;

直接导致表被删除,风险极高。

三、关键使用原则

1. 优先使用#{}

所有常规参数(如查询条件、插入 / 更新的列值)必须用#{},杜绝 SQL 注入风险,同时享受预编译的性能优势。

2. 仅在必要时使用${}

仅当需要动态指定 SQL 语法元素时使用${},且必须做严格的参数校验:

// 对${}参数做白名单校验(示例:仅允许指定的排序字段)
public List<User> getUserList(String sortField) {
    // 白名单
    List<String> allowedFields = Arrays.asList("id", "name", "age");
    if (!allowedFields.contains(sortField)) {
        sortField = "id"; // 默认值,防止注入
    }
    return userMapper.getUserList(sortField);
}

3. 特殊场景的兼容处理

模糊查询#{}可配合CONCAT使用(推荐),而非${}

<!-- 正确:避免注入 -->
SELECT * FROM user WHERE name LIKE CONCAT('%', #{name}, '%')
<!-- 错误:易注入 -->
SELECT * FROM user WHERE name LIKE '%${name}%'

批量插入:MyBatis 的foreach标签中,集合元素用#{}

<insert id="batchInsert">
    INSERT INTO user (name, age) VALUES
    <foreach collection="list" item="item" separator=",">
        (#{item.name}, #{item.age})
    </foreach>
</insert>

四、总结

场景推荐用法注意事项
传递列值 / 条件值#{}自动转义,无注入风险
动态表名 / 列名 / 排序${} + 白名单必须校验参数,限制取值范围
模糊查询#{} + CONCAT避免直接拼接%${}%

核心记住:#{}是 “安全的预编译”,${}是 “危险的字符串拼接”,开发中除非明确需要动态拼接 SQL 语法,否则一律使用#{}

到此这篇关于Mybatis的mapper文件中#和$的区别的文章就介绍到这了,更多相关mybatis #和$区别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • MyBatis-Spring配置的讲解

    MyBatis-Spring配置的讲解

    今天小编就为大家分享一篇关于MyBatis-Spring配置的讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • Springboot+Redis实现API接口防刷限流的项目实践

    Springboot+Redis实现API接口防刷限流的项目实践

    本文主要介绍了Springboot+Redis实现API接口防刷限流的项目实践,通过限流可以让系统维持在一个相对稳定的状态,为更多的客户提供服务,具有一定的参考价值,感兴趣的可以了解一下
    2024-07-07
  • java+jdbc+mysql+socket搭建局域网聊天室

    java+jdbc+mysql+socket搭建局域网聊天室

    这篇文章主要为大家详细介绍了java+jdbc+mysql+socket搭建局域网聊天室,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-01-01
  • Java中集合LinkedList的原理与使用方法

    Java中集合LinkedList的原理与使用方法

    这篇文章主要给大家介绍了关于Java中集合LinkedList的原理与使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-06-06
  • IDEA Maven依赖下载总是失败的几种解决方法

    IDEA Maven依赖下载总是失败的几种解决方法

    我们本地使用 IDEA 运行 maven 项目的时候,有时候运气不好,就会遇到某些 maven 依赖无法正常找到、导入,这就会导致 IDEA 构建项目的时候爆出一堆醒目的红色 Error,今天给大家分享IDEA Maven依赖下载总是失败的几种解决方法,感兴趣的朋友一起看看吧
    2023-09-09
  • IDEA解决src和resource下创建多级目录的操作

    IDEA解决src和resource下创建多级目录的操作

    这篇文章主要介绍了IDEA解决src和resource下创建多级目录的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-02-02
  • Java的SPI机制实例详解

    Java的SPI机制实例详解

    这篇文章主要介绍了Java的SPI机制实例详解的相关资料,需要的朋友可以参考下
    2017-06-06
  • SpringBoot整合JPA框架实现过程讲解

    SpringBoot整合JPA框架实现过程讲解

    在开发中,我们通常会对数据库的数据进行操作,Sprirng Boot对关系型数据库和非关系型数据库的访问操作都提供了非常好的整合支持
    2022-12-12
  • java 创建线程的方法总结

    java 创建线程的方法总结

    这篇文章主要介绍了java 创建线程的方法总结的相关资料,需要的朋友可以参考下
    2017-03-03
  • 使用Java操作Parquet文件的基本步骤

    使用Java操作Parquet文件的基本步骤

    Parquet 是一个强大的列式存储格式,适用于大数据场景,能够高效地进行数据压缩、查询和存储,在 Java 中使用 Apache Spark 读取和写入 Parquet 文件是一项常见的任务,本文给大家介绍了在 Java 中使用 Spark 来读取和写入 Parquet 文件的基本步骤,需要的朋友可以参考下
    2025-03-03

最新评论