Spring Boot JPA+MySQL使用Sequence生成主键出现负ID问题
异常原因
- MySQL本身没有原生SEQUENCE序列对象,使用
GenerationType.SEQUENCE策略时,Hibernate会自动用表模拟序列的方式生成主键,而非直接读取next_val字段值作为主键。 @SequenceGenerator注解未显式配置allocationSize参数时,该值默认是50。Hibernate默认采用块分配的优化策略生成ID,会一次性从序列表申请一段ID值供内存使用,减少表锁竞争。- 你设置的序列表初始值不符合块分配规则:默认50的块大小下,初始
next_val需要至少为50才能生成正整数ID。初始值为0、1这类小于块大小的值时,ID计算逻辑会输出负数,和你遇到的初始值0生成-49、初始值1生成-48的现象完全吻合。
解决方法
根据业务场景选其中一种即可:
方案1:保留序列生成策略,修正配置
低并发场景(ID连续无跳号)
显式将allocationSize设为1,和单步生成的逻辑对齐,不会出现ID预分配:
- 修改实体类主键注解:
@Id @Column(name = "TABLE_TYPE_ID") @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "t_generator") @SequenceGenerator( name = "t_generator", sequenceName = "TABLE_SEQUENCE", initialValue = 1, allocationSize = 1 // 新增配置,指定步长为1 ) private Long tableTypeId; - 重置序列表初始值:
配置完成后ID会从1开始连续递增,无负数、无跳号。TRUNCATE TABLE TABLE_SEQUENCE; INSERT INTO TABLE_SEQUENCE VALUES(1);
高并发场景(性能优先)
保留默认50的块大小,减少序列表的读写锁竞争,只需要修正序列表初始值即可,不需要修改实体代码:
TRUNCATE TABLE TABLE_SEQUENCE; INSERT INTO TABLE_SEQUENCE VALUES(50);
配置后ID从1开始生成,每消耗50个ID才会更新一次序列表,并发性能更好,ID可能存在不连续的情况(应用重启会跳过未使用的预分配ID段)。
方案2:改用MySQL原生自增主键(最适配MySQL)
MySQL原生支持AUTO_INCREMENT自增主键,不需要额外维护序列表,是MySQL场景下的最优选:
- 修改实体类主键策略:
@Id @Column(name = "TABLE_TYPE_ID") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long tableTypeId; - 给业务表的主键字段加自增属性,删除之前手动创建的
TABLE_SEQUENCE表即可。
注意事项
- 用表模拟序列时,必须保证代码中
allocationSize的值和序列表的更新步长一致,否则会出现主键重复、ID异常跳变的问题。 - 如果后续调整
allocationSize的大小,序列表的next_val初始值也要对应调整为不小于allocationSize的值,避免再次出现负数ID。
内容的提问来源于stack exchange,提问作者Subhendu Mahanta
相关产品推荐
相关产品推荐

