手动修改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
相关产品推荐
相关产品推荐

