Flyway-自动递增id不适用于PostgreSQL中的测试数据



在我将Flyway添加到我的项目之前,我可以运行POST请求,并且成功创建了ID为1、下一个ID为2等的新用户。

然后我添加了Flyway来创建表,并通过V1_init.sql:插入一些测试数据

create table "user"(
id int8 not null,
username varchar(255),
);
insert into "user" values (1, 'user1');
insert into "user" values (2, 'user2');
insert into "user" values (3, 'user3');

表已创建。已插入用户。

正在尝试运行POST请求->错误500

org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "organisation_pkey" Key (id)=(1) already exists.

所以我的应用程序应该添加ID为4的新用户,但它似乎无法识别已经添加了3个用户。

我正在使用GenericEntity:

@Getter
@Setter
@MappedSuperclass
public abstract class GenericEntity<ID extends Serializable> implements Serializable {
@Id
@GeneratedValue
protected ID id;
}

application.properties:

spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/my-app
spring.datasource.username=user
spring.datasource.password=user
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.properties.hibernate.format_sql=true

我尝试使用所有策略@GeneratedValue,更改spring.jpa.hibernate.ddl-auto,在init.sql中添加没有id的用户(不工作)

但仍然没有积极作用。有什么想法吗?

你似乎对自己正在做的事情只有一半的了解。。。

我尝试使用所有策略@GeneratedValue

您不需要随机尝试策略,您需要选择与当前数据库设计相匹配的策略。

更改spring.jpa.hibernate.ddl-自动

这很危险,您应该将其设置为";无";,考虑到你使用的是天桥。

在init.sql中添加没有id的用户(不工作)

只有将postgresql设置为自动生成id(这在序列中最简单)时,这才会起作用。从您的代码来看,情况并非如此。

可能出了什么问题?

JPA的@GeneratedValue能够确保在负责创建行时(也就是说在传递EntityManager#persist时)生成值。它不知道也不可能知道您的flyway脚本,在那里您可以绕过JPA手动插入行。

此外,让我们看看@GeneratedValuestrategy属性。您选择的策略将影响JPA生成ID的方式。只有几个选项:TABLESEQUENCEIDENTITYAUTO。由于您没有明确指定策略,因此当前使用的是默认策略AUTO。不建议这样做,因为它不是明确的,而且现在很难说代码在做什么。

TABLESEQUENCE策略下,JPA将与数据库进行交互,以生成ID值。在这些情况下,JPA负责生成值,尽管它将依赖数据库来生成值。不出所料,前者将使用表(顺便说一句,这是罕见的,但也是唯一一种保证在所有RDBMS上工作的策略),后者将使用序列(更常见,几乎所有商业相关的RDBMS都支持它)。对于IDENTITY,JPA根本不会尝试生成密钥,因为此策略假设DB将自己生成ID值。因此,责任完全交给了数据库。这对于具有自己的自动递增机制的数据库来说非常好。

Postgres并没有真正的自动递增系统,但它有一些很好的语法糖,几乎让它像它一样工作:serial";数据类型";。如果将列的数据类型指定为";串行";,事实上,它将使用数据类型int创建,但postgresql也将创建一个序列,并将ID列的默认值绑定到序列的下一个值生成器。

在您的情况下,JPA很可能使用SEQUENCE或TABLE。由于您的DDL设置被设置为";"更新";,Hibernate将在您的背后生成一个表或序列。你应该用类似pgAdmin的东西来检查你的数据库,以验证它是什么,但我会把钱放在一个序列上(所以我假设它使用的是sequence策略)。因为您还没有指定@SequenceGenerator,所以将使用默认值AFAIK,它将从1开始。

然后,当JPA尝试插入新行时,它将调用该序列来生成一个ID值。它将获得序列的下一个值,即1这将与您在飞行通道中手动输入的ID相冲突

我建议的解决方案是:

  • 将postgresql数据类型从int8重新定义为";串行";(实际上是int+a sequence+设置将ID列链接到序列的默认值,这样,如果您没有显式指定一个ID,postgres将自动生成ID。小心,也不要指定null,只是根本不要在insert语句中指定ID列!)
  • 在JPA侧显式地将生成器策略设置为IDENTITY
  • 更新您的flyway脚本以插入没有显式ID值的用户(这将确保测试数据推进序列,这样当JPA稍后使用相同的序列时,它不会生成冲突的ID)

我想说有其他解决方案,但除了使用TABLE策略或在内存中生成密钥(这两件事你都应该避免)之外,没有真正可行的替代方案,因为它无论如何都会归结为使用序列。我想可以手动指定序列,放弃id字段上的默认值,在插入语句中手动调用序列,并在JPA中显式映射序列。。。但我不明白你为什么会让事情变得艰难。

最新更新