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

手动修改H2数据库ID后插入数据报错,如何预检查计算主键ID?

问题原因

用GenerationType.IDENTITY这类依赖数据库自增的策略时,H2会单独维护一个自增计数器,和表中实际数据的ID不联动。你手动把ID从1改成2后,计数器还是停在1,重启插入新数据时,数据库生成的下一个ID是2,刚好和你手动修改的记录冲突,就触发了主键违规异常。

解决方案

1. 临时同步自增计数器

如果只是想快速解决当前问题,直接执行SQL让H2的自增计数器和表中最大ID对齐:

ALTER TABLE TABLE_A ALTER COLUMN ID RESTART WITH (SELECT MAX(ID) + 1 FROM TABLE_A);

执行后,后续插入的ID就会从当前最大ID+1开始生成。

2. 自定义ID生成逻辑(彻底解决手动修改后的冲突)

要让系统自动在插入前检查并计算ID,可以自己写一个ID生成器,每次插入前查当前最大ID再加1:

  • 先写自定义生成器类:
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

public class CustomIdGenerator implements IdentifierGenerator {
    @Override
    public Serializable generate(SharedSessionContractImplementor session, Object object) {
        Connection connection = session.connection();
        try (Statement statement = connection.createStatement()) {
            // 查询当前最大ID并加锁,避免高并发场景下的ID冲突
            ResultSet resultSet = statement.executeQuery("SELECT MAX(ID) FROM TABLE_A FOR UPDATE");
            if (resultSet.next()) {
                int maxId = resultSet.getInt(1);
                return maxId + 1;
            }
        } catch (Exception e) {
            throw new RuntimeException("生成ID失败", e);
        }
        return 1; // 表为空时返回初始ID
    }
}
  • 然后修改实体类的ID注解:
@Id
@GenericGenerator(name = "custom-id", strategy = "你的包路径.CustomIdGenerator")
@GeneratedValue(generator = "custom-id")
private Integer id;

这样每次插入新数据时,都会自动查询当前最大ID并生成下一个值,不会再因为手动修改ID导致冲突。

3. 额外提醒

尽量别手动修改主键ID,这本身就会破坏数据库自增序列的一致性,容易引发这类问题。如果是高并发场景,上面的自定义生成器用了FOR UPDATE加锁,能避免多个请求同时生成相同ID的问题。

内容的提问来源于stack exchange,提问作者Bora Ciner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 08:33:32