获取 mysql(innodb) AUTO_INCREMENT Column、JDBC/getGeneratedKeys()/last_insert_id (in OkPacket) 和 LAST_I

Posted

技术标签:

【中文标题】获取 mysql(innodb) AUTO_INCREMENT Column、JDBC/getGeneratedKeys()/last_insert_id (in OkPacket) 和 LAST_INSERT_ID() 的方法【英文标题】:Ways to get mysql(innodb) AUTO_INCREMENT Column, JDBC/getGeneratedKeys()/last_insert_id (in OkPacket) and LAST_INSERT_ID() 【发布时间】:2020-10-25 19:27:09 【问题描述】:

根据 mysql 文档: https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-usagenotes-last-insert-id.html

有时,使用 SELECT LAST_INSERT_ID() 查询可能会很棘手, 因为该函数的值被限定为一个连接。所以,如果其他一些 查询发生在同一个连接上,该值被覆盖。在 另一方面,getGeneratedKeys() 方法的范围是 Statement 实例,因此即使其他查询发生在同一个实例上,它也可以使用 连接,但不在同一个 Statement 实例上。

首先,我考虑LAST_INSERT_ID()。 SQL 函数LAST_INSERT_ID() 是连接安全的,但不是会话/事务/语句安全的。它不能在生产中使用,因为在实际环境中,一个连接中的多个会话/事务/语句非常常见。

然后getGeneratedKeys() 使用 JDBC。当我在 Java 中使用 getGeneratedKeys() 时。我想看看它在数据库中的作用。在使用 JDBC 使用自动增加主键简单插入演示表后,我尝试使用以下语句跟踪 SQL 语句:

SET GLOBAL log_output = 'TABLE'; 
SET GLOBAL general_log = 'ON';
SELECT * FROM mysql.general_log;

我确定新行已正确插入,getGeneratedKeys() 会带回自动递增的 id。但是,我只发现 JDBC 之前执行的插入语句,以及一些静态数据,如 "SELECT database(),version()..."。 现在,结论是,getGeneratedKeys() 不执行任何 SQL 语句来获取自动递增的 id。然后我找到了另一种可能性,我调试到调用堆栈,请参阅 JDBC get auto-incremented id from an object called OkPacket。它有一个名为last_insert_id 的属性。终于找到了。

我的问题是:

    真的没有办法使用纯 SQL 语句(不使用 JDBC)获得 STATEMENT SAFE(至少是事务安全)自动递增 id 吗? OkPacket 如何在后台工作?它如何获得声明安全自动增加ID?也许它在 MySQL 驱动程序或 MySQL 服务器/客户端协议中调用了一些低级 C 函数?

【问题讨论】:

【参考方案1】:

MySQL 有一个 API,客户端使用该 API 来传达命令并获取结果。

其实这个API有两种形式。一种称为“SQL 协议”,其中语句作为 SELECT * FROM mytable 等字符串发送。另一种形式称为“二进制协议”,其中命令使用服务器识别的某个字节发送,即使它们不是人类可读的字符串。

有些命令可以通过 SQL 协议或二进制协议执行。 例如,START TRANSACTIONCOMMITPREPARE...这些命令有文本 SQL 语句,但 API 调用这些命令也有非文本方式。

您当然可以查询SELECT LAST_INSERT_ID(); 并获取最新生成的id,但是最新的。当您阅读时,另一个 INSERT 语句将覆盖此值。

OkPacket 由二进制协议填写。也就是说,MySQL 服务器返回一个 OkPacket,其中包含有关任何语句执行的几条元数据。

见https://github.com/mysql/mysql-connector-j/blob/release/8.0/src/main/protocol-impl/java/com/mysql/cj/protocol/a/result/OkPacket.java#L55

Ok 数据包包括以下内容:

受影响的行数(如果有) 最后插入 ID(如果有) 状态标志 警告计数(如果有) 带有错误消息的字符串(如果有)

记录 OK 数据包的 MySQL 服务器代码非常详尽,并附有示例:

https://github.com/mysql/mysql-server/blob/8.0/sql/protocol_classic.cc#L665-L838

无法获取早期 SQL 语句的 OK 数据包。客户端必须在语句执行后立即保存结果。在面向对象的代码(如 JDBC 驱动程序)中,将其存储在 NativeResultset 对象中是有意义的:https://github.com/mysql/mysql-connector-j/blob/release/8.0/src/main/protocol-impl/java/com/mysql/cj/protocol/a/result/NativeResultset.java#L77-L82

【讨论】:

以上是关于获取 mysql(innodb) AUTO_INCREMENT Column、JDBC/getGeneratedKeys()/last_insert_id (in OkPacket) 和 LAST_I的主要内容,如果未能解决你的问题,请参考以下文章

当涉及的字段之一为 NULL 时,MySQL 错误地允许重复条目

Mysql设置auto_increment_increment和auto_increment_offset

mysql 约束条件 auto_increment 自动增长起始值 布长 起始偏移量

获取 mysql(innodb) AUTO_INCREMENT Column、JDBC/getGeneratedKeys()/last_insert_id (in OkPacket) 和 LAST_I

获取mysql的配置使用多少 RAM

mysql性能优化------获取mysql的行数多少