在我将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手动插入行。
此外,让我们看看@GeneratedValue
的strategy
属性。您选择的策略将影响JPA生成ID的方式。只有几个选项:TABLE
、SEQUENCE
、IDENTITY
和AUTO
。由于您没有明确指定策略,因此当前使用的是默认策略AUTO
。不建议这样做,因为它不是明确的,而且现在很难说代码在做什么。
在TABLE
和SEQUENCE
策略下,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中显式映射序列。。。但我不明白你为什么会让事情变得艰难。