MySQL开启RewriteBatchedStatements后PreparedStatement的一个异常

【问题描述】:

MySQL 开启 RewriteBatchedStatements 属性后,PreparedStatement在解析一种Insert形式的SQL时发生异常,测试代码如下,使用的MySQL驱动为mysql-connector-java-5.1.36-bin.jar:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

public class Demo
{
    public static final String DBDRIVER = "com.mysql.jdbc.Driver";
    public static final String DBURL = "jdbc:mysql://127.0.0.1:3306/S10?rewriteBatchedStatements=true";
    public static final String DBUSER = "root";
    public static final String DBPASS = "123456";

    public static void main(String[] args)
    {
        try
        {
            Class.forName(DBDRIVER);
            try (Connection conn = DriverManager.getConnection(DBURL, DBUSER, DBPASS);
                    PreparedStatement pstmt = conn.prepareStatement(
                            "INSERT INTO _1033 SET F01 = ?, F02 =? ON DUPLICATE KEY UPDATE F02 = VALUES(F02)"))

            {
                pstmt.setLong(1, 1);
                pstmt.setString(2, "2");
                pstmt.executeUpdate();
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
        catch (ClassNotFoundException e)
        {
            e.printStackTrace();
        }
    }
}

异常信息为

java.sql.SQLException: java.lang.StringIndexOutOfBoundsException: String index out of range: -36
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:998)
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:937)
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:926)
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:872)
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:904)
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:894)
	at com.mysql.jdbc.Util.handleNewInstance(Util.java:418)
	at com.mysql.jdbc.PreparedStatement.getInstance(PreparedStatement.java:762)
	at com.mysql.jdbc.ConnectionImpl.clientPrepareStatement(ConnectionImpl.java:1455)
	at com.mysql.jdbc.ConnectionImpl.prepareStatement(ConnectionImpl.java:4205)
	at com.mysql.jdbc.ConnectionImpl.prepareStatement(ConnectionImpl.java:4109)
	at org.querydemo.QueryDemo.main(QueryDemo.java:22)
Caused by: java.lang.StringIndexOutOfBoundsException: String index out of range: -36
	at java.lang.String.substring(String.java:1911)
	at com.mysql.jdbc.PreparedStatement$ParseInfo.extractValuesClause(PreparedStatement.java:442)
	at com.mysql.jdbc.PreparedStatement$ParseInfo.buildRewriteBatchedParams(PreparedStatement.java:371)
	at com.mysql.jdbc.PreparedStatement$ParseInfo.<init>(PreparedStatement.java:358)
	at com.mysql.jdbc.PreparedStatement$ParseInfo.<init>(PreparedStatement.java:175)
	at com.mysql.jdbc.PreparedStatement.<init>(PreparedStatement.java:836)
	at com.mysql.jdbc.JDBC4PreparedStatement.<init>(JDBC4PreparedStatement.java:45)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
	at com.mysql.jdbc.Util.handleNewInstance(Util.java:400)
	... 5 more

跟踪代码,发现MySQL的驱动程序在开启 RewriteBatchedStatements 属性后,会对满足如下条件的SQL进行重写

protected static boolean canRewrite(String sql, boolean isOnDuplicateKeyUpdate, int locationOfOnDuplicateKeyUpdate, int statementStartPos)
{
  boolean rewritableOdku = true;
  
  if (isOnDuplicateKeyUpdate) {
    int updateClausePos = StringUtils.indexOfIgnoreCase(locationOfOnDuplicateKeyUpdate, sql, " UPDATE ");
    
    if (updateClausePos != -1) {
      rewritableOdku = StringUtils.indexOfIgnoreCase(updateClausePos, sql, "LAST_INSERT_ID", "\"‘`", "\"‘`", StringUtils.SEARCH_MODE__MRK_COM_WS) == -1;
    }
  }
  
  return (StringUtils.startsWithIgnoreCaseAndWs(sql, "INSERT", statementStartPos)) && (StringUtils.indexOfIgnoreCase(statementStartPos, sql, "SELECT", "\"‘`", "\"‘`", StringUtils.SEARCH_MODE__MRK_COM_WS) == -1) && (rewritableOdku);
}

重写的方法可以参考com.mysql.jdbc.PreparedStatement类的下面两个方法

private void buildRewriteBatchedParams(String sql, MySQLConnection conn, DatabaseMetaData metadata, String encoding, SingleByteCharsetConverter converter)
private String extractValuesClause(String sql, String quoteCharStr) throws SQLException


【问题解决】:

修正的措施是将

PreparedStatement pstmt = conn.prepareStatement("INSERT INTO _1033 SET F01 = ?, F02 =? ON DUPLICATE KEY UPDATE F02 = VALUES(F02)")

改为

PreparedStatement pstmt = conn.prepareStatement("INSERT INTO _1033 (F01, F02 ) VALUES (?, ?) ON DUPLICATE KEY UPDATE F02 = VALUES(F02)")
时间: 2024-10-14 02:30:02

MySQL开启RewriteBatchedStatements后PreparedStatement的一个异常的相关文章

mysql 开启事务后update出现的错误

解决方法: 首先,查看数据库的进程信息: show full processlist; 在执行: SELECT * FROM information_schema.INNODB_TRX\G;注意结果中的trx_mysql_thread_id部分的值 查找对应的id,然后kill id的值即可;

mysql开启缓存

mysql 开启查询缓存可以有两种方法来开启一种是使用set命令来进行开启,另一种是直接修改my.ini文件来直接设置都是非常的简单的哦. 开启缓存,设置缓存大小,具体实施如下: 1.修改配置文件,windows下是my.ini,linux下是my.cnf; 在配置文件的最后追加上:  代码如下 复制代码 query_cache_type = 1 query_cache_size = 600000 需要重启mysql生效: 那么采用第二种方式: b) 开启缓存,两种方式: a)使用mysql命令

mysql数据库误删除后的数据恢复操作说明

在日常运维工作中,对于mysql数据库的备份是至关重要的!数据库对于网站的重要性使得我们对mysql数据的管理不容有失!然后,是人总难免会犯错误,说不定哪天大脑短路了来个误操作把数据库给删除了,怎么办??? 下面,就mysql数据库误删除后的恢复方案进行说明. 一.工作场景(1)MySQL数据库每晚12:00自动完全备份.(2)某天早上上班,9点的时候,一同事犯晕drop了一个数据库!(3)需要紧急恢复!可利用备份的数据文件以及增量的binlog文件进行数据恢复. 二.数据恢复思路(1)利用全备

mysql插入数据后返回自增ID的方法

mysql插入数据后返回自增ID的方法 mysql和oracle插入的时候有一个很大的区别是,oracle支持序列做id,mysql本身有一个列可以做自增长字段,mysql在插入一条数据后,如何能获得到这个自增id的值呢? 方法一:是使用last_insert_id mysql> SELECT LAST_INSERT_ID(); 产生的ID 每次连接后保存在服务器中.这意味着函数向一个给定客户端返回的值是该客户端产生对影响AUTO_INCREMENT列的最新语句第一个 AUTO_INCREMEN

xtrabackup 备份mysql数据库三: innobackupex 测试一个全量和两个增量的备份恢复测试

## 查看当前库中表的数据 ([email protected]) [test]>select count(*) from t_innodb; +----------+ | count(*) | +----------+ |        0 | +----------+ 1 row in set (0.00 sec) ## 执行插入数据操作,该操作在全备之后执行完成 ([email protected]) [test]>call addTest(100000,0); ## 执行全库备份 #

mysql开启远程可连接

1.打开cmd窗口,进入MySql安装的bin目录 2.执行命令登录数据库,之后会出现一行要你输入密码的 mysql -u root -p 3.执行以下命令分配新用户: grant all privileges on *.* to '用户名'@'IP地址' identified by '密码'; 4.执行完上述命令后用下面的命令刷新权限 flush privileges; 5.之后关闭mysql服务,然后启动mysql服务,大功告成 ===============================

MySQL 误操作后如何快速恢复数据~!~!~

基本上每个跟数据库打交道的程序员(当然也可能是你同事)都会碰一个问题,MySQL误操作后如何快速回滚?比如,delete一张表,忘加限制条件,整张表没了.假如这还是线上环境核心业务数据,那这事就闹大了.误操作后,能快速回滚数据是非常重要的. 传统解法 用全量备份重搭实例,再利用增量binlog备份,恢复到误操作之前的状态.然后跳过误操作的SQL,再继续应用binlog.此法费时费力,不值得再推荐. 利用binlog2sql快速闪回 首先,确认你的MySQL server开启了binlog,设置了

mysql开启事件

SET GLOBAL event_scheduler = 1; 注意:事件开启后会在mysql服务重启后自动关闭.一般在重启服务器后再执行一次即可

网站环境apache + php + mysql 的XAMPP,如何实现一个服务器上配置多个网站?

xampp 是一个非常方便的本地 apache + php + mysql 的调试环境,在本地安装测试 WordPress 等各种博客.论坛程序非常方便.今天我们来给大家介绍一下,如何使用 XAMPP 在本地进行安装多个网站. 一般情况下,我们只需要网站程序放到 xampp/htdoc 目录下,然后在浏览器里输入 ip 地址 http://127.0.0.1/ 或者输入域名 http://localhost/ 就可以了.但是这样我们只能使用一个程序,建立一个网站.如果我们想要测试测试不同的程序,