返回顶部
首页 > 资讯 > 后端开发 > Python >Mybatis中单双引号引发的惨案及解决
  • 931
分享到

Mybatis中单双引号引发的惨案及解决

2024-04-02 19:04:59 931人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

目录#{}与${}的区别问题最后#{}与${}的区别 #{}是预编译处理,${}是字符串替换mybatis在处理#{}时,会将sql中的#{}替换为?号, 调用PreparedSta

#{}与${}的区别

#{}是预编译处理,${}是字符串替换mybatis在处理#{}时,会将sql中的#{}替换为?号, 调用PreparedStatement的set方法来赋值;

Mybatis在处理时 , 就 是 把 {}时,就是把时,就是把{}替换成变量的值。

使用#{}可以有效的防止SQL注入,提高系统安全性。

再通俗的说,使用${}mybatis会把参数加上双引号,而${} 你给啥,sql语句中就是啥,如下示例:

select * from table where name = #{name}  name->小明 
## 结果:select * from table where name = "小明"
select * from table where name = ${name}  name->小明 
## 结果:select * from table where name = 小明

问题

最近有个功能需要从sqlserver中去数据,有个脚本很简单如下:

select * from table where id in(...) 

id已经创建索引了,考虑到数据传输,我每次设置的集合大小为100个,因为这是再简单不过的语句了,直接上线给别人使用,但是别人的反馈是,使用50个id需要40多秒!!! 这就有点吓人了,幸好此场景只是在半夜定时的去使用,慢一点不会对第二天有影响,但是白天想要测试的时候就懵了。当然了40多s就别提是否影响别人使用了,基本上就已经崩溃了好不好!!!

这就有点吓人了,幸好此场景只是在半夜定时的去使用,慢一点不会对第二天有影响,但是白天想要测试的时候就懵了。当然了40多s就别提是否影响别人使用了,基本上就已经崩溃了好不好!!!

下面简化了一下,对应的xml代码如下:

<select id="selectTbdIdByLbdIdList" resultType="xxx.xxx.xxMapper">
    SELECT id ,tid FROM table where id IN
    <foreach collection="list" item="item" open="(" close=")" separator=",">
        #{item}
    </foreach>
</select>

debug 模式下的输出如下:

| ==>  Preparing: SELECT id ,tid FROM table where id IN ( ?,?,?,?,?,?...) 
| ==> Parameters: 123(String),234(String),345(String),456(String),
| <==      Total: ....

我把sql整理出来放在sqlserver客户端去执行

SELECT id ,tid FROM table where id IN ( "123","234","345"...);

刚开始执行报错了,后面把双引号改成单引号就行了,即

SELECT id ,tid FROM table where id IN ( '123','234','345'...);
耗时: 0.092s

记住这里的单双引号的问题

??? 很快啊,这是什么情况,第一次遇到这种情况,直接运行sql很快,但是通过mybatis就很慢。

所以我首先怀疑是ORM框架的问题,接着我用JDBC快速写了个demo,来验证,代码如下:

String connectionUrl = "jdbc:sqlserver://xxx:8838;DatabaseName=xxx;user=xxx;passWord=xx";
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
Connection con = DriverManager.getConnection(connectionUrl);
Statement stmt = con.createStatement();
String SQL = "SELECT id ,tid FROM table where id IN ( '123','234','345'...)";
long s = System.nanoTime();
ResultSet rs = stmt.executeQuery(SQL);
System.out.println((System.nanoTime() - s) / 1_000_000);
// Iterate through the data in the result set and display it.
while (rs.next()) {
    System.out.println(rs.getString("id") + " ---> " + rs.getString("tid"));
}
// 耗时0.109ms

这里也是很快,没什么问题,忽略ORM的问题。

因为我这里用的是Mybatis-Plus,所以我又怀疑是mp的问题,于是debug代码,最后卡在这个地方:

//PreparedStatementHandler.class
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();// 卡在这一行
    return resultSetHandler.handleResultSets(ps);
}

但这是Mybatis的代码,再者说mp只是简化了代码生成这一块,对Mybatis本身的执行没有影响,所以mp也被排除!

这个时候已经过去很长时间了,整个人很懵,怎么会这样???这么简单的sql还会出这么大的问题!我重新理了下思绪,此处的sql是在sqlserver上执行的,那会不会是sqlserver上的问题呢?

我突然灵光一闪,刚刚debug出来的脚本直接放在sqlserver的客户端上执行的时候是有问题的,我后面是把双引号改成单引号才成功的,我赶紧调整了xml中的脚本,如下:

<select id="selectTbdIdByLbdIdList" resultType="xxx.xxx.xxMapper">
    SELECT id ,tid FROM table where id IN
    <foreach collection="list" item="item" open="(" close=")" separator=",">
        '${item}'
    </foreach>
</select>

然后再执行,debug出来的脚本如下:

| ==>  Preparing: SELECT id ,tid FROM table where id IN ( '123','234','345','456'...) 
| ==> Parameters: 
| <==      Total: ....

耗时: 0.100s!!!

如释重负,原来是双引号惹的祸!

SqlServer是不支持双引号的,但是mybatis最后生成的sql使用的双引号,当然这对Mysql是没问题的,当然也有例外

如果SQL服务器模式启用了NSI_QUOTES,可以只用单引号引用字符串。用双引号引用的字符串被解释为一个识别符。

所以我遇到的情况是就是生成带双引号的脚本丢给sqlserver执行的时候,被sql服务器误认为是一个识别符,类似java中类型的强转,此时索引是不生效的,也就是说一开的in查询时没有使用到索引的!!!话说那个表中有700w条记录,怪不得每次查询50条的时候,耗时很均匀,都在40多秒。。。。。

回到开头,这种情况就是借助${}来解决,当然是用它是有隐患的,因为它并不能防止sql注入,但是对于我这边的场景不会出现这种情况,所以我赶紧的把其他地方也都改了过来!!!

最后

解决问题还是要大胆假设,小心求证 事实的真相只有一个!!!

另外在debug的时候,顺便看到了#{}和${}的拼接代码,放在了下面

// ForEachSqlnode
public void appendSql(String sql) {
    GenericTokenParser parser = new GenericTokenParser("#{", "}", content -> {
        String newContent = content.replaceFirst("^\\s*" + item + "(?![^.,:\\s])", itemizeItem(item, index));
        if (itemIndex != null && newContent.equals(content)) {
            newContent = content.replaceFirst("^\\s*" + itemIndex + "(?![^.,:\\s])", itemizeItem(itemIndex, index));
        }
        return "#{" + newContent + "}";
    });
    delegate.appendSql(parser.parse(sql));
}
// TextSqlNode  
private GenericTokenParser createParser(TokenHandler handler) {
  return new GenericTokenParser("${", "}", handler);
}
// GenericTokenParser
public String parse(String text) {
    if (text == null || text.isEmpty()) {
        return "";
    }
    // search open token
    int start = text.indexOf(openToken);
    if (start == -1) {
        return text;
    }
    char[] src = text.toCharArray();
    int offset = 0;
    final StringBuilder builder = new StringBuilder();
    StringBuilder expression = null;
    while (start > -1) {
        if (start > 0 && src[start - 1] == '\\') {
            // this open token is escaped. remove the backslash and continue.
            builder.append(src, offset, start - offset - 1).append(openToken);
            offset = start + openToken.length();
        } else {
            // found open token. let's search close token.
            if (expression == null) {
                expression = new StringBuilder();
            } else {
                expression.setLength(0);
            }
            builder.append(src, offset, start - offset);
            offset = start + openToken.length();
            int end = text.indexOf(closeToken, offset);
            while (end > -1) {
                if (end > offset && src[end - 1] == '\\') {
                    // this close token is escaped. remove the backslash and continue.
                    expression.append(src, offset, end - offset - 1).append(closeToken);
                    offset = end + closeToken.length();
                    end = text.indexOf(closeToken, offset);
                } else {
                    expression.append(src, offset, end - offset);
                    offset = end + closeToken.length();
                    break;
                }
            }
            if (end == -1) {
                // close token was not found.
                builder.append(src, start, src.length - start);
                offset = src.length;
            } else {
                builder.append(handler.handleToken(expression.toString()));
                offset = end + closeToken.length();
            }
        }
        start = text.indexOf(openToken, offset);
    }
    if (offset < src.length) {
        builder.append(src, offset, src.length - offset);
    }
    return builder.toString();
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

--结束END--

本文标题: Mybatis中单双引号引发的惨案及解决

本文链接: https://lsjlt.com/news/162476.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Mybatis中单双引号引发的惨案及解决
    目录#{}与${}的区别问题最后#{}与${}的区别 #{}是预编译处理,${}是字符串替换Mybatis在处理#{}时,会将sql中的#{}替换为号, 调用PreparedStat...
    99+
    2024-04-02
  • 基于mybatis中test条件中单引号双引号的问题
    目录test条件中单引号双引号问题具体原因动态sql中test的一些问题mybatis动态sql中OGNL中type=="1"和type='1'的...
    99+
    2024-04-02
  • python中的单引号、双引号和多引号
    目录 python中的单引号 python中的双引号 python中的多引号 三者分别在什么时候使用,有什么区别 总结 python中的单引号 在Python中,单引号(`'`)可以用来表示字符串。 可以使用单引号创建一个简单的字符串...
    99+
    2023-10-20
    python
  • PHP中单引号和双引号的区别详解
    目录PHP中单引号和双引号简介PHP中字符串和变量插值时单引号和双引号的区别PHP中转义撇号时单引号和双引号的区别补充知识:字符串内部如果出现PHP引号怎么办--关于转义.总结本文将...
    99+
    2023-01-15
    php单引号和双引号区别在哪 php单双引号的区别 php中双引号和单引号
  • 单引号与双引号在PHP中的差异及影响
    单引号与双引号在PHP中的差异及影响 在PHP中,单引号(')和双引号(")被用来表示字符串。虽然它们看起来很相似,但在使用时却存在一些差异和影响。本文将详细介绍单引号和双引号在PHP...
    99+
    2024-03-05
    php字符串 引号使用 编程注意事项
  • Redis中一个String类型引发的惨案
    ​ 曾经看到这么一个案例,有一个团队需要开发一个图片存储系统,要求这个系统能快速记录图片ID和图片存储对象ID,同时还需要能够根据图片的ID快速找到图片存储对象ID。我们...
    99+
    2024-04-02
  • 区分 PHP 中的单引号和双引号
    本文将介绍 PHP 中单引号和双引号的区别。 PHP 中单引号和双引号简介 在 PHP 中,我们使用引号来指定值是字符串文字。有两种不同类型的报价。它们是单引号、'和双引号"...
    99+
    2024-02-27
  • MySQL 中一个双引号的错位引发的血案
    来源:For DBAwww.fordba.com/mysql-double-quotation-marks-accident.html一、前言最近经常碰到开发误删除误更新数据,这不,他们又给我找了个麻烦,我...
    99+
    2024-04-02
  • PHP中单引号与双引号的区别及应用场景
    PHP中单引号与双引号是两种用于表示字符串的不同方式,它们在一些方面有一些细微的区别。在本文中,我们将探讨PHP中单引号与双引号的区别以及它们在不同场景下的应用。 首先,我们来看看单引...
    99+
    2024-03-05
    php中引号区别 php引号应用 php引号选择
  • PHP中的单引号和双引号的区别
    PHP中单引号和双引号简介PHP中字符串和变量插值时单引号和双引号的区别PHP中转义撇号时单引号和双引号的区别 本文将介绍PHP中单引号和双引号的区别。 PHP中单引号和双引号简介 在 PHP 中,我们使用引号来指定值是字符串文字。有两种不...
    99+
    2023-09-29
    php servlet 开发语言
  • PHP中单引号和双引号的使用规则解析
    在PHP中,单引号和双引号是两种常见的字符串包裹方式,它们在使用时有着不同的特点和规则。本文将分别对单引号和双引号的使用规则进行解析,并提供具体的代码示例来帮助读者更好地理解它们的区别...
    99+
    2024-03-05
    php字符串 双引号 单引号
  • linux shell中单引号、双引号、反引号、反斜杠的区别
    1. 单引号 ( '' ) # grep Susan phonebook Susan Goldberg 403-212-4921 Susan Topple 212-234-2343 如果我们想查找的是Sus...
    99+
    2022-06-04
    斜杠 引号 区别
  • oracle中单引号和双引号的区别在哪
    oracle中,单引号主要用于定义字符串值和标识符,而双引号则提供转义字符处理、标识符冲突解决和字符串连接等功能:转义字符处理:双引号允许使用转义字符,单引号则不能。标识符:单引号用于对...
    99+
    2024-05-07
    oracle
  • oracle中双引号与单引号的区别在哪
    oracle 数据库中,双引号引用对象标识符(如表名),单引号定义字符串文字。单引号字符串中的单引号需转义,区分大小写,不能字符拼接;双引号字符串不区分大小写,可拼接。 Oracle ...
    99+
    2024-05-07
    oracle
  • c语言中单引号和双引号的用法
    摘要:c 语言中的单引号和双引号用于定义字符串常量,单引号定义长度受限的字符数组,存储于数据区,可修改;双引号定义存储于代码区的字符串常量,长度不受限,不可修改,可包含转义字符。 单引...
    99+
    2024-05-02
    c语言 字符串常量
  • php中的双引号与单引号的基本使用
    字符串,在各类编程语言中都是一个非常重要的数据类型 网页当中的图片,文字,特殊符号,HTMl标签,英文等都属于字符串 PHP字符串变量用于存储并处理文本, 在创建字符串之后,我们就可以对它进行操作。我们可以直接在函数中使用字符串,或...
    99+
    2023-08-31
    php 数学建模 开发语言
  • oracle导入导出表时因一个分号引发的惨案
     oracle 如何导入导出表 在数据库中导出表后导入,是一个完整的操作,内容中的oracle 11g是安装在windows 上的。 oracle的imp/exp就相当于oracle数据的还原与备...
    99+
    2024-04-02
  • javascript中的单引号和双引号有什么区别
    本篇内容介绍了“javascript中的单引号和双引号有什么区别”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所...
    99+
    2024-04-02
  • php中的单引号与双引号有什么区别
    这篇文章主要介绍“php中的单引号与双引号有什么区别”,在日常操作中,相信很多人在php中的单引号与双引号有什么区别问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”php中的单...
    99+
    2024-04-02
  • 在 PHP 中单引号(‘ ‘)和双引号(“ “)用法的区别
    在 PHP 中,使用单引号(' ')和双引号(" ")可以创建字符串。这两种引号的用法有一些区别。 单引号: 单引号用于创建简单的字符串,其中的变量和转义字符将不会被解析。单引号中的任何内容都被视为普通文本,不会进行任何特殊处理。使用单...
    99+
    2023-09-02
    php
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作