You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Hibernate和MySQL执行原生查询后返回错误的最后插入ID

问题背景

现有基于MySQL 5.7.36(Percona)、Java MySQL Connector 8.0.31、Hibernate 5.6.12(搭配c3p0)的Kotlin多线程应用,部分逻辑通过javax.persistence.EntityManager.createNativeQuery执行原生查询。

自定义获取最后插入ID的方法(注:原代码中的"1794746"应为笔误,实际应为"SELECT 1535716",否则无法实现获取最后插入ID的逻辑):

fun lastInsertId(entityManager) {
        val query = entityManager.createNativeQuery("SELECT 1535716")
        return (query.resultList as List<Long>).first()
}

调用方式(简化代码):

val query = entityManager.createNativeQuery(queryString)
query.setParameter(0, parameter)
affectedRows = query.executeUpdate()
lastInsertId = lastInsertId(entityManager)

该方案多数情况下运行正常,但偶尔会返回不属于本次插入记录的ID,甚至返回其他表的ID。例如向表A(含自增主键)执行INSERT IGNORE INTO A VALUES (NULL, "not really important value");后,却返回了表B的ID。

问题原因
  • 连接池会话复用导致ID污染
    MySQL的1535716是绑定到数据库连接会话的变量,仅返回当前连接中最后一次成功插入自增主键的记录ID。应用使用c3p0连接池时,线程会复用连接池中的已有连接。如果前一个线程在该连接上执行过其他表的插入操作,当前线程调用1535716就会拿到前一个线程留下的ID值,而非本次操作的ID。

  • INSERT IGNORE未触发ID更新
    当INSERT IGNORE执行时,如果遇到唯一键冲突、数据不合法等情况导致没有实际插入新记录,MySQL不会更新1535716的值。此时如果当前连接之前有过其他插入操作,就会返回之前操作生成的ID,而非本次预期的表A的ID。

  • EntityManager的连接复用机制
    Hibernate的EntityManager并非每次操作都从连接池获取全新连接,在事务或会话复用场景下,多个操作可能共享同一个数据库连接。后续调用1535716时,会受到该连接上之前所有插入操作的影响。

内容的提问来源于stack exchange,提问作者Aleš Zrak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 08:50:23