接入Flyway后PostgreSQL测试数据导致自增ID失效重复主键报错
问题原因
- PostgreSQL的自增主键依赖序列对象实现,未指定策略的
@GeneratedValue注解在PostgreSQL方言下,Hibernate默认会使用名为hibernate_sequence的全局序列生成主键,序列初始值为1。 - 接入Flyway前,
spring.jpa.hibernate.ddl-auto=update配置会自动处理主键的自增关联逻辑,插入数据时Hibernate生成ID会同步推进序列值,所以ID可以正常递增。 - 接入Flyway后,你手动编写的建表语句没有给id字段设置自增属性(如
serial8/GENERATED AS IDENTITY),手动插入ID=1、2、3的测试数据时不会触发序列递增,序列的当前值依旧停留在1。 - 新增用户时Hibernate从序列取到的第一个值就是1,插入时和已存在的ID冲突,触发报错。
解决方案
可根据业务场景任选一种:
- 方案一:使用表独立自增序列
修改Flyway建表语句,给id字段指定自增属性,同时删除多余的语法逗号:
插入测试数据后追加SQL重置该表关联序列的当前值:create table "user"( id serial8 not null primary key, username varchar(255) );
同时修改主键生成策略为IDENTITY:insert into "user" values (1, 'user1'); insert into "user" values (2, 'user2'); insert into "user" values (3, 'user3'); -- 重置序列到当前最大ID SELECT setval('user_id_seq', (SELECT MAX(id) FROM "user"));@Id @GeneratedValue(strategy = GenerationType.IDENTITY) protected ID id; - 方案二:继续使用Hibernate全局序列
不需要修改建表和实体类配置,只需在V1_init.sql脚本末尾追加SQL重置全局序列的当前值:
该方案适合多表共用同一个全局序列生成主键的场景。SELECT setval('hibernate_sequence', (SELECT MAX(id) FROM "user")); - 方案三:插入测试数据时不指定ID
修改插入语句,让数据库自动生成ID,会自动推进对应序列的当前值:insert into "user" (username) values ('user1'); insert into "user" (username) values ('user2'); insert into "user" (username) values ('user3');
内容的提问来源于stack exchange,提问作者fat_pikachu
相关产品推荐
相关产品推荐

