获取 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 TRANSACTION
、COMMIT
、PREPARE
...这些命令有文本 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