mysql中#{} 和 ${} 区别?

 作者简介:大家好,我是码炫码哥,前中兴通讯、美团架构师,现任某互联网公司CTO,兼职码炫课堂主讲源码系列专题


代表作:《jdk源码&多线程&高并发》,《深入tomcat源码解析》,《深入netty源码解析》,《深入dubbo源码解析》,《深入springboot源码解析》,《深入spring源码解析》,《深入redis源码解析》等


联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬。码炫课堂的个人空间-码炫码哥个人主页-面试,源码等

回答

#{}${}是 MyBatis 两种不同的占位符,#{} 为预编译处理,而${} 是字符串替换。

#{}${}
用途传递参数,防止 SQL 注入传递参数,不能防止 SQL 注入
实现机制MyBatis 将#{}中的内容替换为 ?,并在运行时通过PreparedStatement 的 set 方法设置参数值。MyBatis 将${}中的内容直接替换为参数值,即将参数直接拼接到 SQL 字符串中
安全性安全性高,可以有效防止 SQL 注入。存在安全隐患,直接拼接 SQL 字符串容易导致 SQL 攻击。

使用场景

#{}用在动态 SQL 参数赋值,防止 SQL 注入。

@Select("SELECT * FROM t_user where id = #{id}")
User selectUserById(@Param("id") Long id);

// #{} 解析替换为 ? ===> SELECT * FROM t_user where id = ?

${}用在需将参数直接拼接在 SQL 的场景(如动态表名、列名、排序)。如按月对订单表拆分后的查询场景

SELECT * FROM ${table_name} where create_time >= #{createTime}

// 当订单按月分表后,可根据日期拼接表名(如 order_202407)再查询数据。

扩展

#{}和 ${}如何被解析?

  • SqlSource 接口及其实现

DynamicSqlSource:处理动态 SQL 语句(一是包含$占位符的表达式,二是包含9种动态标签中的任何一个)。

public BoundSql getBoundSql(Object parameterObject) {
    // 动态上下文
    DynamicContext context = new DynamicContext(configuration, parameterObject);
    // 动态SQL标签处理,解析静态SQL
    rootSqlNode.apply(context);
    // sql源解析器
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
    // 解析为StaticSqlSource(生成预处理SQL)
    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    context.getBindings().forEach(boundSql::setAdditionalParameter);
    return boundSql;
}

public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
    // 参数token处理器(通过方法handleToken将#{}替换为?)
    ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
    // 定义通用解析器
    GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
    String sql;
    // 动态 SQL 解析,替换 #{} 为 ?
    if (configuration.isShrinkWhitespacesInSql()) {
      sql = parser.parse(removeExtraWhitespaces(originalSql));
    } else {
      sql = parser.parse(originalSql);
    }
    return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}

Mybatis 支持动态 SQL 吗?

  • RawSqlSource:不是DynamicSqlSource,便会被解析为 RawSqlSource。
public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
    // sql源解析器
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> clazz = parameterType == null ? Object.class : parameterType;
    // 解析为StaticSqlSource(生成预处理SQL)
    sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());
}
  • PropertyParser:处理${}的变量替换。
  • Mybatis 将 XML 定义的 SQL 包装XNode。解析时,Mybatis 调用了内部函数 parseAttributes() 和 parseBody(),这两个方法内部调用了静态方法 PropertyParser#parse 来解析每一个 Node 节点属性。
private Properties parseAttributes(Node n) {
    Properties attributes = new Properties();
    // 遍历 Node 节点的属性
    NamedNodeMap attributeNodes = n.getAttributes();
    if (attributeNodes != null) {
      for (int i = 0; i < attributeNodes.getLength(); i++) {
        Node attribute = attributeNodes.item(i);
        // 使用 PropertyParser 解析占位符值
        String value = PropertyParser.parse(attribute.getNodeValue(), variables);
        attributes.put(attribute.getNodeName(), value);
      }
    }
    return attributes;
}

public static String parse(String string, Properties variables) {
    // 内部类实现 tokenHandler,支持 ${key:default} 形式的解析
    VariableTokenHandler handler = new VariableTokenHandler(variables);
    // 使用 GenericTokenParser 来解析占位符属性
    GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
    return parser.parse(string);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值