MyBatis(6)#{}和${}的区别

简介: 在MyBatis中,`#{}`和`${}`是用于在SQL语句中嵌入参数的两种方式。`#{}`用于预处理参数,可以防止SQL注入;而`${}`进行直接字符串替换,适用于动态插入表名或列名,但存在SQL注入风险。建议优先使用`#{}`,并在必要时谨慎使用`${}`。

在MyBatis中,#{}${}是用于在SQL语句中嵌入参数的两种不同方式。它们的核心区别在于预处理和潜在的SQL注入风险。

#{}(预处理)

#{}用于预处理参数(prepared statement),也就是说,参数占位符会被替换为?,然后参数值会在执行时绑定到SQL语句中。这样做的好处是可以防止SQL注入,因为MyBatis会对参数进行适当的转义处理。

以下是使用#{}的代码示例:

java

代码解读

复制代码

@Select("SELECT * FROM user WHERE id = #{id}")
User getUserById(@Param("id") Integer id);

MyBatis会将上面的SQL语句转换为一个预处理语句(prepared statement),大致相当于:

sql

代码解读

复制代码

SELECT * FROM user WHERE id = ?

然后,MyBatis会将id参数的值安全地绑定到问号(?)位置。

${}(直接替换)

${}进行的是直接字符串替换。你提供的字符串会在MyBatis创建SQL语句之前就被替换到SQL中。这种方式允许你动态地插入表名、列名或者是动态的SQL片段。但由于这种方式可能会导致SQL注入风险,它的使用需要非常小心。

以下是使用${}的代码示例:

java

代码解读

复制代码

@Select("SELECT * FROM ${tableName} WHERE id = ${id}")
User getUserById(@Param("tableName") String tableName, @Param("id") Integer id);

如果tableName是"user",id是1,那么最终的SQL将会是:

sql

代码解读

复制代码

SELECT * FROM user WHERE id = 1

源码分析

当MyBatis解析#{}${}时,它使用了不同的解析器。对于#{},MyBatis使用ParameterMapping来处理每一个参数,将其转换为一个预处理的参数。

对于${},MyBatis将参数的实际值直接拼接到SQL字符串中,这就意味着如果参数包含特殊字符,它们将直接嵌入到SQL中,可能引起安全问题。

在MyBatis的源码中,SqlSourceBuilder 类处理带有#{}的表达式:

java

代码解读

复制代码

public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
    ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
    TokenParser parser = new TokenParser("#{", "}", handler);
    String sql = parser.parse(originalSql);
    StaticSqlSource sqlSource = new StaticSqlSource(configuration, sql, handler.getParameterMappings());
    return sqlSource;
}

这里,ParameterMappingTokenHandler 负责将#{}标记的参数转换为预处理参数,并创建相应的ParameterMapping

而对于${}的处理,简单的字符串替换是通过TextSqlNode处理的:

java

代码解读

复制代码

public boolean apply(DynamicContext context) {
    Matcher matcher = pattern.matcher(text);
    StringBuilder builder = new StringBuilder();
    while (matcher.find()) {
        String replacement = getProperty(matcher.group(1), context.getBindings());
        if (replacement != null) {
            matcher.appendReplacement(builder, replacement);
        } else {
            // handle null value ...
        }
    }
    matcher.appendTail(builder);
    context.appendSql(builder.toString());
    return true;
}

在这里,getProperty方法直接从上下文中取出变量值并替换掉${}标记的部分。

细节和最佳实践

  • 应尽可能使用#{}来防止SQL注入攻击。
  • 只有在需要动态替换表名、列名或者SQL片段时才考虑使用${}
  • 如果必须使用${},确保参数值来自于信任的源,或者对参数值进行严格的验证和清理,以避免SQL注入风险。
  • 在可能的情况下,考虑使用MyBatis的内置功能,如<if>标签和<choose>标签等,来动态构建SQL语句,而不是依赖${}

总之,在编写安全的MyBatis应用时,理解#{}${}的区别是至关重要的,以确保你的应用不容易受到SQL注入攻击。


转载来源:https://juejin.cn/post/7384256110281293876

相关文章
|
6月前
|
SQL Java 数据库连接
hibernate和mybatis的区别
hibernate和mybatis的区别
|
2月前
|
SQL XML Java
mybatis复习02,简单的增删改查,@Param注解多个参数,resultType与resultMap的区别,#{}预编译参数
文章介绍了MyBatis的简单增删改查操作,包括创建数据表、实体类、配置文件、Mapper接口及其XML文件,并解释了`#{}`预编译参数和`@Param`注解的使用。同时,还涵盖了resultType与resultMap的区别,并提供了完整的代码实例和测试用例。
mybatis复习02,简单的增删改查,@Param注解多个参数,resultType与resultMap的区别,#{}预编译参数
|
3月前
|
SQL Java 数据库连接
MyBatis 和 Hibernate 有什么区别?
【8月更文挑战第21天】
65 0
|
3月前
|
SQL Java 数据库连接
在mybatis中#{}和${}的区别
在MyBatis中,使用#{}可以防止SQL注入,它通过预处理语句来安全地设置参数值,而${}会将传入的数据直接插入SQL语句中,不安全,通常用于传入数据库对象或在确保数据安全的情况下使用。
|
5月前
|
SQL XML Java
蓝易云 - ibatis与mybatis的区别
总的来说,MyBatis在功能性和易用性上都优于iBatis,是iBatis的改进版。
41 2
|
6月前
|
SQL Java 数据库连接
MyBatis与Hibernate区别
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
|
5月前
|
SQL Java 数据库连接
【MyBatis】MyBatis操作数据库(二):动态SQL、#{}与${}的区别
【MyBatis】MyBatis操作数据库(二):动态SQL、#{}与${}的区别
62 0
|
6月前
|
SQL 缓存 Java
|
SQL Java 数据库连接
MyBatis之动态SQL、#与$的区别和结果映射
MyBatis之动态SQL、#与$的区别和结果映射
115 0
|
SQL 安全 Java
Mybatis的动态SQL及关键属性和标识的区别(对SQL更灵活的使用)
Mybatis的动态SQL及关键属性和标识的区别(对SQL更灵活的使用)
55 0