【问题标题】:Avoid duplicate primary keys after database intialization in spring-boot 2spring-boot 2中数据库初始化后避免重复主键
【发布时间】:2025-12-30 12:35:07
【问题描述】:

默认的@GeneratedValue策略用于spring boot 1.5 web app,没有任何类型的重复id冲突

...使用一个简单的实体,比如这个

// in my/package/Car.java
// ...
@Entity
public class Car {
    private long id;
    private String company;
    private String model;

    @Id
    @GeneratedValue
    public long getId() {
        return id;
    }

    // ... more getters and setters
}

... 并在启动时使用

初始化数据库
# in src/main/resources/import.sql
insert into car values (1, 'Tesla', 'Roadster');

...然后插入另一辆车

Car c = new Car();
c.setCompany("Ford");
c.setModel("Pinto");
entityManager.persist(c);
entityManager.flush();

// expect no issue inserting, and a valid ID
log.info("Assigned ID is " + c.getId());

... 用于生成一个新的Car,ID 为2。我并不真正关心生成的 ID,只要没有冲突。但是,同样的代码现在会引发以下异常:

org.hsqldb.HsqlException: integrity constraint violation: unique constraint or index violation; SYS_PK_10095 table: CAR (数据库是 HSQL,我宁愿不用替换它)

...因为default sequence generation in hibernate 5.2 现在不考虑现有插入。

我有哪些可能的解决方法来仍然允许通过import.sql 初始化数据库?我知道我可以

  • 在初始化时使用非常大的 id(但这只是把罐子踢下去,并不是真正的解决方案:最终序列会赶上并破坏事物)
  • 编写我自己的序列生成器(但必须有更简单的方法来初始化数据库!)
  • 使用旧的序列生成(但同样,如果这样做没有优势,他们为什么要更改它?hibernate 开发人员肯定有一些更好的初始化方法!)。
  • 以某种方式为新 ID 指定一个起始值(如何以故障安全的方式执行此操作?是否有一个属性可以进入我的 application.properties 以保持集中?)

我想在 spring-boot web 应用程序的上下文中使用它,并使其尽可能简单并接近最佳实践。有什么建议吗?

【问题讨论】:

  • 您好,我不确定您的问题是否正确,但我遇到了类似的问题,错误是我通过 SQl 语句插入了一个元素。在此语句中,ID 设置为固定值,但据我所知,您的 GeneratedValue 由 Spring 管理,它无法识别手动设置的 ID。因此,当您使用 Spring 上下文插入元素时,GeneratedValue 序列将从 1 开始并引发错误,因为 ID 已经存在。
  • @MatthiasLauber 是的,这就是问题所在。但是,我需要能够插入 SQL 语句,因为否则我不能包含关系(需要 ID 来匹配外键值)。否则,您将如何使用 ID 初始化数据库?另一种初始化 ID 的方法是有效的答案。
  • 您使用 Flyway 吗?在那里你为你的表编写一个 SQL 模式。您可以定义一个序列并告诉您的表使用序列的 nex 值生成 ID 并将序列插入您的 Spring 实体中
  • @MatthiasLauber 我没有使用 Flyway,并希望最大限度地减少依赖关系。但是,这似乎是一个可行的解决方案:谢谢。

标签: spring hibernate spring-boot jpa


【解决方案1】:

从版本 5 开始,使用 SEQUENCE 代替 IDENTITY 生成 id。 Migration from Hibernate 4 to 5

发生了什么?

您使用脚本插入了 ID 为 1 的记录。序列保持在 1。它想要插入 1 导致唯一 PK 违规的内容。

解决方案

不要使用自动生成类型。使用身份。然后通过脚本插入记录,IDENTITY 会自动增加。你也不需要插入 ID 值:

 DECLARE temp_id INTEGER;
 INSERT INTO CUSTOMERS VALUES (DEFAULT, firstname, lastname, CURRENT_TIMESTAMP);
 SET temp_id = IDENTITY();
 INSERT INTO ADDRESSES VALUES (DEFAULT, temp_id, address);

【讨论】:

  • 是的,我已经测试过这个重复的主键是问题所在(如果 ID 从 insert.sql 中的 10 开始,则没有发现问题;这是我列表中的第 1 点变通方法,如果有足够的时间显然会失败)。使用 IDENTITY 似乎是可能的,但是我不明白为什么我不需要插入 ID:我还能如何通过 import.sql 添加关系?是否有另一种方法来初始化没有 ID 但具有一对多和多对多关系的数据库?
  • 可以使用函数 IDENTITY() 获得最后插入到连接标识列中的值
  • 链接似乎已关闭;我发现了这个:hsqldb.org/doc/guide/builtinfunctions-chapt.html;但是,我仍然不知道如何在import.sql 文件中使用它来表示Car 1 由Person 2 驱动,假设Person_Car 关系采用car_id 和@ 987654331@