9

问题概述

在看似随机的时间,我们得到一个异常“postgresql 重复键违反唯一约束”。我确实认为我知道我们的问题是什么,但我不想在没有可重现的测试用例的情况下更改代码。但由于除了在生产中随机之外,我们无法在任何环境中重现它,我正在向 SO 寻求帮助。

在这个项目中,我们有多个 postgres 数据库,并为每个数据库中的每个表配置了一个主键序列。这些序列是这样创建的:

create sequence PERSONS_SEQ;
create sequence VISITS_SEQ;
etc...

我们使用这些序列为实体生成主键,如下所示:

@Entity
@Table(name = "visits")
public class Visit {
  @Id
  @Column(name = "id")
  @SequenceGenerator(name = "seq", sequenceName = "visits_seq")
  @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq")
  private int id;
  ...
}

@Entity
@Table(name = "person")
public class Person {
  @Id
  @Column(name = "id")
  @SequenceGenerator(name = "seq", sequenceName = "persons_seq")
  @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq")
  private int id;
  ...
}

分析

我想我认识到此配置存在 2 个问题:

1) 两个@SequenceGenerators 都指定相同的名称属性,即使它们应该映射到不同的数据库序列。

2)@SequenceGenerator allocationSize 属性默认为 50(我们使用 hibernate 作为 JPA 提供程序)所以我认为创建序列语法应该指定序列应该增加多少,特别是 50 以匹配 allocationSize。

基于这个猜测,我认为代码应该修改成这样:

create sequence PERSONS_SEQ increment by 50;
create sequence VISITS_SEQ increment by 50;
etc...

@Entity
@Table(name = "visits")
public class Visit {
  @Id
  @Column(name = "id")
  @SequenceGenerator(name = "visits_seq", sequenceName = "visits_seq")
  @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "visits_seq")
  private int id;
  ...
}

@Entity
@Table(name = "person")
public class Person {
  @Id
  @Column(name = "id")
  @SequenceGenerator(name = "persons_seq", sequenceName = "persons_seq")
  @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "persons_seq")
  private int id;
  ...
}

我只是对此进行测试,而不是在 SO 上提出问题,但同样,我们无法在任何其他环境中重现此生产问题。即使在生产中,唯一约束违规也只会在看似随机的时间发生。

问题:

1)我对修复这种独特约束违规的更改应该是什么的分析是否正确?

2)当使用hibernate作为JPA提供者时,使用序列生成器的最佳实践是什么?

4

4 回答 4

5
  1. Yes, your analysis is correct. You identified correctly the problem (we had a similar problem). And... if you gonna put that in production, don't forget to:

    • either generate manually the sequence table for the new sequence generator WITH the correct initial value/initial ID (otherwise hibernate will begin from 1 and you will get again )
    • or set that value in Code (check initalValue in @SequenceGenerator).
  2. I am not able to enumerate the best practices, but I suppose you could lower the limit of 50. Also I do not have experience with PostgreSQL, but in MySQL you have a simple table for the seq. generator and hibernate makes the entire stuff.

于 2013-09-25T16:56:50.500 回答
4

有同样的问题——由于某种原因,hibernate 没有从序列中选择正确的数字。尝试了所有方法都没有运气,最后得出了这个解决方案:

@Entity
@Table(name = "events")
@SequenceGenerator(name = "events_id_seq", sequenceName = "events_id_seq", allocationSize = 1)
public class Event {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "events_id_seq")
    private BigInteger id;

我不得不将@SequenceGenerator 放在类的顶部,而不是方法上,分配大小也设置为1(如果您将此值保留为默认值,它将开始产生负ID)。

spring-data-jpa 2.1.2, hibernate 5.3.7,pg 42.2.5

于 2019-02-06T18:31:48.647 回答
1

我有一个类似的问题。就我而言,我直接通过 SQL 导入数据。这导致了“hibernate_sequence”的问题。hibernate_sequence 的 id 为 123,但我的表中有一些行的 id 大于 123。

于 2016-11-08T23:14:27.957 回答
0

我遇到了同样的问题。我试过这个来解决这个问题。可能这不是最好的解决方案,但我希望它现在可以解决您的问题。

@SequenceGenerator(schema = "DS_TEST",name = "SEQ_PR_TEXT",sequenceName = "SEQ_PR_TEXT",
        allocationSize = 1)
public class TextEntity {
    @Id
    @GeneratedValue(generator = SequenceConstant.SEQ_PR_TEXT,
            strategy = GenerationType.SEQUENCE)
    @Column(name = "PR_TEXT_ID")
    private Long id;
}
于 2020-02-17T14:51:21.413 回答