获取 mysql(innodb) AUTO_INCREMENT 列的方法,JDBC/getGeneratedKeys()/last_insert_id(在 OkPacket 中)和 LAST_INSERT_ID()
Ways to get mysql(innodb) AUTO_INCREMENT Column, JDBC/getGeneratedKeys()/last_insert_id (in OkPacket) and LAST_INSERT_ID()
根据 mysql 文档:
https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-usagenotes-last-insert-id.html
At times, it can be tricky to use the SELECT LAST_INSERT_ID() query,
as that function's value is scoped to a connection. So, if some other
query happens on the same connection, the value is overwritten. On the
other hand, the getGeneratedKeys() method is scoped by the Statement
instance, so it can be used even if other queries happen on the same
connection, but not on the same Statement instance.
首先,我考虑LAST_INSERT_ID()
。
SQL 函数 LAST_INSERT_ID()
是连接安全的,但不是 session/transaction/statement 安全的。它不能用于生产,因为在实际环境中,一个连接中有多个 session/transaction/statement 是很常见的。
然后 getGeneratedKeys()
使用 JDBC。当我在 Java 中使用 getGeneratedKeys()
时。我想看看它在数据库中做了什么。在使用 JDBC:
自动增加主键的演示 table 中简单插入后,我尝试使用以下语句跟踪 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 从一个名为 OkPacket
的对象中获取自动递增的 id。它有一个名为 last_insert_id
的 属性。终于找到了
我的问题是:
- 真的没有办法使用纯 SQL 语句(没有 JDBC)获得 STATEMENT SAFE(至少事务安全)自动递增的 ID 吗?
OkPacket
是如何工作的?它如何获得声明安全的自动增加的ID?也许它在 MySQL 驱动程序或 MySQL server/client 协议中调用了一些低级 C 函数?
MySQL 有一个 API 客户端用来传达命令和获取结果。
其实这个API有两种形式。一种称为“SQL 协议”,其中语句以 SELECT * FROM mytable 等字符串的形式发送。另一种形式称为“二进制协议”,其中使用一些字节发送命令服务器识别,即使它们不是人类可读的字符串。
一些命令可以通过SQL协议或二进制协议执行。
例如,START TRANSACTION
、COMMIT
、PREPARE
...这些命令有文本 SQL 语句,但 [=55= 也有非文本方式] 调用这些命令。
您当然可以查询 SELECT LAST_INSERT_ID();
并获取最新生成的 id,但 仅 是最新的。在您阅读时,另一个 INSERT 语句将覆盖此值。
OkPacket
是二进制协议填写的。也就是说,MySQL 服务器 returns 一个 OkPacket,其中包含关于任何语句执行的几条元数据。
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 文档: https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-usagenotes-last-insert-id.html
At times, it can be tricky to use the SELECT LAST_INSERT_ID() query, as that function's value is scoped to a connection. So, if some other query happens on the same connection, the value is overwritten. On the other hand, the getGeneratedKeys() method is scoped by the Statement instance, so it can be used even if other queries happen on the same connection, but not on the same Statement instance.
首先,我考虑LAST_INSERT_ID()
。
SQL 函数 LAST_INSERT_ID()
是连接安全的,但不是 session/transaction/statement 安全的。它不能用于生产,因为在实际环境中,一个连接中有多个 session/transaction/statement 是很常见的。
然后 getGeneratedKeys()
使用 JDBC。当我在 Java 中使用 getGeneratedKeys()
时。我想看看它在数据库中做了什么。在使用 JDBC:
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 从一个名为 OkPacket
的对象中获取自动递增的 id。它有一个名为 last_insert_id
的 属性。终于找到了
我的问题是:
- 真的没有办法使用纯 SQL 语句(没有 JDBC)获得 STATEMENT SAFE(至少事务安全)自动递增的 ID 吗?
OkPacket
是如何工作的?它如何获得声明安全的自动增加的ID?也许它在 MySQL 驱动程序或 MySQL server/client 协议中调用了一些低级 C 函数?
MySQL 有一个 API 客户端用来传达命令和获取结果。
其实这个API有两种形式。一种称为“SQL 协议”,其中语句以 SELECT * FROM mytable 等字符串的形式发送。另一种形式称为“二进制协议”,其中使用一些字节发送命令服务器识别,即使它们不是人类可读的字符串。
一些命令可以通过SQL协议或二进制协议执行。
例如,START TRANSACTION
、COMMIT
、PREPARE
...这些命令有文本 SQL 语句,但 [=55= 也有非文本方式] 调用这些命令。
您当然可以查询 SELECT LAST_INSERT_ID();
并获取最新生成的 id,但 仅 是最新的。在您阅读时,另一个 INSERT 语句将覆盖此值。
OkPacket
是二进制协议填写的。也就是说,MySQL 服务器 returns 一个 OkPacket,其中包含关于任何语句执行的几条元数据。
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