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 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> mybatis中${}和#{}源码分析 -> 正文阅读

[大数据]mybatis中${}和#{}源码分析

1.参考示例
2.${}和#{}解析以及赋值对比分析
????2.1 sql解析对比
????2.2 sql赋值分析
3.关于${}和#{}模糊查询方式梳理说明
????源码视角搞清楚为什么#{}可以防止sql注入,直接进入主题!

1.参考示例

????示例方法:

// 根据标题模糊查询资讯信息
List<News> findNews(String title);

????配置文件:

<!--${}模糊查询-->
<select id="findNews" resultType="com.it.txm.demo.controller.News">
   select id,title from find_news where title  like '%${title1}%'
</select>
<!--#{}模糊查询-->
  <select id="findNews" resultType="com.it.txm.demo.controller.News">
    select id,title from find_news
    where title like "%"#{title}"%"
    </select>

????测试案例:

List<News> news = newsMapper.findNews("abc");
		System.out.println(news);

2. ${}和#{}解析以及赋值对比分析

mybatis配置文件解析赋值流程
在这里插入图片描述

GenericTokenParser.java中parse方法执行解析处理(mybatis配置文件加载,具体sql执行之前两者都会执行此方法,含有${}的sql执行赋值操作也会执行此方法).

public String parse(String text) {
    if (text == null || text.isEmpty()) {
      return "";
    }
    //判断sql中是否包含${}或#{},如果没有则结束,继续解析下一个节点
    int start = text.indexOf(openToken);
    if (start == -1) {
      return text;
    }
    // 省略部分代码
    // sql中含有${}或#{}处理方式不同.前者使用PropertyParser解析,后者使用SqlSourceBuilder解析
     builder.append(handler.handleToken(expression.toString()));
   // 省略部分代码
    return builder.toString();
  }

2.1 sql解析对比

????对于#{}处理,会将#{title}替换成?,对应源码:
SqlSourceBuilder.java中handleToken

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

????对于${}解析处理,最终只会进行参数拼接:
PropertyParser.java中handleToken

public String handleToken(String content) {
      if (variables != null) {
        String key = content;
        if (enableDefaultValue) {
          final int separatorIndex = content.indexOf(defaultValueSeparator);
          String defaultValue = null;
          if (separatorIndex >= 0) {
            key = content.substring(0, separatorIndex);
            defaultValue = content.substring(separatorIndex + defaultValueSeparator.length());
          }
          if (defaultValue != null) {
            return variables.getProperty(key, defaultValue);
          }
        }
        if (variables.containsKey(key)) {
          return variables.getProperty(key);
        }
      }
      return "${" + content + "}";
    }
  }

2.2 sql赋值对比

????${}赋值处理,获取值具体对应源码:
TextSqlNode.java

 public String handleToken(String content) {
      Object parameter = context.getBindings().get("_parameter");
      if (parameter == null) {
        context.getBindings().put("value", null);
      } else if (SimpleTypeRegistry.isSimpleType(parameter.getClass())) {
        context.getBindings().put("value", parameter);
      }
      Object value = OgnlCache.getValue(content, context.getBindings());
      String srtValue = value == null ? "" : String.valueOf(value); // issue #274 return "" instead of "null"
      checkInjection(srtValue);
      return srtValue;
    }

获取值之后重新走GenericTokenParser.java中parse进行参数拼接.拼接后sql如下:
在这里插入图片描述

#{}赋值处理:
????按照 string类型进行赋值处理,i表示第几个参数,将?替换成对应的实参.具体对应源码:
StringTypeHandler.java中setNonNullParameter

 public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
      throws SQLException {
    ps.setString(i, parameter);
  }

????需要注意的地方是对于传递的参数参数是字符串时,会将双引号替换为单引号.
ClientPreparedQueryBindings.java执行上面具体的setString逻辑

public void setString(int parameterIndex, String x) {
  			// 省略部分代码,下面是对特殊字符进行转义处理
            for (int i = 0; i < stringLength; ++i) {
                char c = x.charAt(i);

                switch (c) {
                    case 0: /* Must be escaped for 'mysql' */
                        buf.append('\\');
                        buf.append('0');
                        break;
                    case '\n': /* Must be escaped for logs */
                        buf.append('\\');
                        buf.append('n');
                        break;
                    case '\r':
                        buf.append('\\');
                        buf.append('r');
                        break;
                    case '\\':
                        buf.append('\\');
                        buf.append('\\');
                        break;
                    case '\'':
                        buf.append('\'');
                        buf.append('\'');
                        break;
                    case '"': /* Better safe than sorry */
                        if (this.session.getServerSession().useAnsiQuotedIdentifiers()) {
                            buf.append('\\');
                        }
                        buf.append('"');
                        break;
                    case '\032': /* This gives problems on Win32 */
                        buf.append('\\');
                        buf.append('Z');
                        break;
                    case '\u00a5':
                    case '\u20a9':
                        // escape characters interpreted as backslash by mysql
                        if (this.charsetEncoder != null) {
                            CharBuffer cbuf = CharBuffer.allocate(1);
                            ByteBuffer bbuf = ByteBuffer.allocate(1);
                            cbuf.put(c);
                            cbuf.position(0);
                            this.charsetEncoder.encode(cbuf, bbuf, true);
                            if (bbuf.get(0) == '\\') {
                                buf.append('\\');
                            }
                        }
                        buf.append(c);
                        break;

                    default:
                        buf.append(c);
                }
            }

            buf.append('\'');

            parameterAsString = buf.toString();
        }
// 将双引号参数替换成单引号
        byte[] parameterAsBytes = this.isLoadDataQuery ? StringUtils.getBytes(parameterAsString)
                : (needsQuoted ? StringUtils.getBytesWrapped(parameterAsString, '\'', '\'', this.charEncoding)
                        : StringUtils.getBytes(parameterAsString, this.charEncoding));

        setValue(parameterIndex, parameterAsBytes, MysqlType.VARCHAR);
    }
}

赋值完成之后的sql如下

????至此,${}和#{}sql解析以及参数赋值梳理完毕.后面查询以及结果集封装操作相同,在此不再展开.

3.关于 ${}和#{}模糊查询方式梳理说明

????${}模糊查询,无论单引还是双引,执行结果相同

<select id="findNews" resultType="com.it.txm.demo.controller.News">
    select id,title from find_news
    where title  like "%${title1}%"
</select>
<select id="findNews" resultType="com.it.txm.demo.controller.News">
    select id,title from find_news
    where title  like '%${title1}%'
</select>

????#{}模糊查询,下面两种执行正常

 <select id="findNews" resultType="com.it.txm.demo.controller.News">
        select id,title from find_news
        where title like concat('%',#{title},'%')
    </select>
<select id="findNews" resultType="com.it.txm.demo.controller.News">
select id,title from find_news
where title like "%"#{title}"%"
</select>

????如果单引的方式会由于解析问题导致查询不到指定内容(本例中查询结果为空),平常注意一下即可.

   <select id="findNews" resultType="com.it.txm.demo.controller.News">
    select id,title from find_news
    where title like '%'#{title}'%'
    </select>

????欢迎小伙伴评论区留言,共同探讨,相互学习!

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-01-17 11:34:11  更:2022-01-17 11:34:26 
 
开发: 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年11日历 -2024/11/24 14:31:19-

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