java - 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 个用户。
我正在使用通用实体:
@Getter
@Setter
@MappedSuperclass
public abstract class GenericEntity<ID extends Serializable> implements Serializable {
@Id
@GeneratedValue
protected ID id;
}
应用程序属性:
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,在没有 id 的 init.sql 中添加用户(不起作用)
但仍然没有积极的影响。有什么想法可能是错的吗?
解决方案
您似乎对自己在做什么只有一半的了解...
我尝试使用所有策略@GeneratedValue
您不需要随机尝试策略,您需要选择与您当前的数据库设计相匹配的策略。
改变 spring.jpa.hibernate.ddl-auto
这是危险的,您应该将其设置为“无”,因为您正在使用飞行路线。
在没有 id 的 init.sql 中添加用户(不工作)
这只有在 postgresql 设置为自动生成 ids 时才有效(通过序列最简单)。从您的代码来看,情况并非如此。
有什么问题?
JPA@GeneratedValue
能够确保在它负责创建行时生成值(这意味着当您通过 时EntityManager#persist
)。它不知道也不能知道您绕过 JPA 手动插入行的 flyway 脚本。
此外,让我们看看@GeneratedValue
' 的strategy
属性。您选择的策略将影响JPA生成 ID 的方式。只有几个选项:TABLE
、SEQUENCE
和。由于您没有明确指定策略,因此您当前使用的是默认策略,即. 不推荐这样做,因为它不明确,现在很难说你的代码在做什么。IDENTITY
AUTO
AUTO
在TABLE
andSEQUENCE
策略下,JPA 将与数据库进行交互以生成 ID 值。在这些情况下,JPA 负责生成值,尽管它将依赖数据库来完成。不出所料,前者将使用一个表(这很罕见,顺便说一句,但也是保证在所有 RDBMS 上工作的唯一策略),后者将使用一个序列(更常见并且几乎每个商业相关的 RDBMS 都支持)。使用IDENTITY
,JPA 根本不会尝试生成密钥,因为此策略假定数据库将自行生成 ID 值。因此,责任完全委托给了数据库。这对于具有自己的自动增量机制的数据库来说非常有用。
Postgres 并没有真正的自动增量系统,但它有一些很好的语法糖,几乎使它像它一样工作:serial
“数据类型”。如果将列的数据类型指定为“serial”,它实际上将使用数据类型 int 创建,但 postgresql 也会创建一个序列并将 ID 列的默认值绑定到序列的下一个值生成器。
在您的情况下,JPA 很可能使用 SEQUENCE 或 TABLE。由于您的 DDL 设置设置为“更新”,Hibernate 将在您背后生成一个表或序列。你应该用 pgAdmin 之类的东西检查你的数据库来验证它是什么,但我会把钱放在一个序列上(所以我假设它使用的是 SEQUENCE 策略)。因为您没有指定 a @SequenceGenerator
,所以将使用默认值,AFAIK 将从 1 开始。
然后,当 JPA 尝试插入新行时,它将调用该序列以生成 ID 值。它将获得序列的下一个值,即 1。这将与您在 flyway 中手动输入的 ID 冲突。
我推荐的解决方案是:
- 将您的 postgresql 数据类型从 int8 重新定义为“serial”(实际上是 int + 一个序列 + 设置将 ID 列链接到序列的默认值,以便如果您没有明确指定一个,postgres 将自动生成一个 ID - 小心,也不要指定
null
,根本不要在插入语句中指定 ID 列!) IDENTITY
在 JPA 端显式设置生成器策略- 更新你的 flyway 脚本以插入没有明确 ID 值的用户(这将确保测试数据推进序列,这样当 JPA 稍后使用相同的序列时,它不会生成冲突的 ID)
我想说有替代解决方案,但除了使用TABLE
策略或在内存中生成键(您应该避免这两件事)之外,没有真正可行的替代方案,因为无论如何它都会归结为使用序列。我想可以手动指定序列,放弃 id 字段上的默认值,在插入语句中手动调用序列,并在 JPA 中显式映射序列......但我不明白你为什么要做事情对自己狠。
推荐阅读
- c++ - QAbstractProxyModel 为第二个 QTreeView 重新排序数据
- java - 重载、覆盖、多态的关系?
- mysql - 为什么我无法连接到我的 AWS RDS 实例?
- javascript - Sveltestrap Tooltip 生成 ReferenceError: process not defined (in createPopper.js)
- vba - VBA将公式应用于范围并查找输出总和
- python - 如何在 Python 中从 post 请求中检索图像数据
- java - 将整数连接成字符串的最快方法
- php - 捕获重复模式的正则表达式问题
- java - 使用 pojo 从纯字符串创建 JSON
- python - 在特定的父 Xml Python 下创建子元素