IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> mybatis中 #{}和${}的区别 -> 正文阅读

[Java知识库]mybatis中 #{}和${}的区别

1:?#{}和${}的区别

首先配置在UserMapper.xm中配置l两个sql:

<mapper namespace="com.demo.mybatis.UserMapper">
?? ??? ?<select id="selectUser" resultType="com.demo.mybatis.domain.User">
?? ??? ???? ??? ?? ?select * from user where id = #{id}
?? ??? ?</select>
?? ??? ?<select id="selectUserById" timeout="0" resultType="User">
?? ??? ??? ?select * from user where id = ${id}
?? ??? ?</select>
</mapper>

测试代码:

    @Test
    public void dollarAndPound() {
        //         select * from user where id = #{id}
        String selectUser = "com.demo.mybatis.UserMapper.selectUser";
        //  select * from user where id = ${id}
        String selectUserById = "com.demo.mybatis.UserMapper.selectUserById";
        List users1 =  sqlSessionTest.selectList(selectUser,"1 or 1=1 ");
        List users2 =  sqlSessionTest.selectList(selectUserById,"1 or 1=1");
        System.out.println(users1.size());
        System.out.println(users2.size());
    }

测试结果

22:45:13.624 [main] DEBUG com.demo.mybatis.UserMapper.selectUser - ==>  Preparing: select * from user where id = ?
22:45:13.654 [main] DEBUG com.demo.mybatis.UserMapper.selectUser - ==> Parameters: 1 or 1=1 (String)
22:45:13.674 [main] DEBUG com.demo.mybatis.UserMapper.selectUser - <==      Total: 1
after....
before....
22:45:15.194 [main] DEBUG com.demo.mybatis.UserMapper.selectUserById - ==>  Preparing: select * from user where id = 1 or 1=1
22:45:15.194 [main] DEBUG com.demo.mybatis.UserMapper.selectUserById - ==> Parameters: 
22:45:15.194 [main] DEBUG com.demo.mybatis.UserMapper.selectUserById - <==      Total: 6

执行结果截然不同,使用$的sql不能防止sql注入,这是为什么呢?

我们要知道mybatis在处理#和$的sql的语句是怎样的流程。
首先mybatis在解析UserMapper.xml的时候读取所有的sql语句节点,并解析,代码如下
XMLMapperBuilder.java

  private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }

遍历所得Sql节点,XMLScriptBuilder对sqlNode执行解析生成sqlSource

public SqlSource parseScriptNode() {
    MixedSqlNode rootSqlNode = parseDynamicTags(context);
    SqlSource sqlSource;
    if (isDynamic) {
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
  }

这里会判断是否是动态sql,isDynamic的条件如下:
TextSqlNode.java中

  public boolean isDynamic() {
    DynamicCheckerTokenParser checker = new DynamicCheckerTokenParser();
    GenericTokenParser parser = createParser(checker);
    parser.parse(text);
    return checker.isDynamic();
  }

private GenericTokenParser createParser(TokenHandler handler) {? 
   return new GenericTokenParser("${", "}", handler)0;
 }

这里很清楚了,在sql中包含${}的最终会判断为isDynamic的sql
在DynamicSqlSource方法getBoundSql()中rootSqlNode.apply(context)完成sql拼接,
在这里是将参数 1 or 1=1拼接到 select * from user where id = 后面;

  @Override
  public BoundSql getBoundSql(Object parameterObject) {
    DynamicContext context = new DynamicContext(configuration, parameterObject);
    rootSqlNode.apply(context);
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    context.getBindings().forEach(boundSql::setAdditionalParameter);
    return boundSql;
  }

如果是RawSqlSource 中构造方法:

  public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> clazz = parameterType == null ? Object.class : parameterType;
    sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());
  }

产生SqlSourceBuilder 解析sql的parse()方法中使用ParameterMappingTokenHandler

对象来处理sql

 @Override
    public String handleToken(String content) {
      parameterMappings.add(buildParameterMapping(content));
      return "?";
    }

将#{}部分都用?代替,实现预编译sql。
到这里sql中 #与 的 处 理 梳 理 完 毕 。 那 么 既 然 的处理梳理完毕。 那么既然 会出现sql注入的问题,那么什么场景下可以受那个$动态sql呢?
1 将表名,列名当作参数使用的场景。
select * from ${tableName};这里就不能使用#了
2 group by的指定字段排序。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-07-30 12:36:58  更:2021-07-30 12:38:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/2 1:11:04-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码