JPA 2 @SequenceGenerator @GeneratedValue产生唯一约束违规

时间:2013-09-24 23:41:55

标签: java hibernate postgresql jpa

问题概述

在看似随机的时间我们得到一个例外“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;
  ...
}

分析

我认为我认识到这个配置有两个问题:

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

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

根据这个猜测,我认为应该将代码修改为这样的代码:

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 个答案:

答案 0 :(得分:4)

  1. 是的,您的分析是正确的。您正确识别了问题(我们遇到了类似的问题)。 并且......如果你要把它投入生产,不要忘记:

    • 使用正确的初始值/初始ID手动生成新序列生成器的序列表(否则hibernate将从1开始,您将再次获得)
    • 或在代码中设置该值(检查initalValue中的@SequenceGenerator)。
  2. 我无法列举最佳实践,但我想你可以降低50的限制。另外我没有PostgreSQL的经验,但在MySQL中你有一个简单的seq表。生成器和休眠使整个东西。

答案 1 :(得分:1)

我有类似的问题。在我的例子中,我直接通过SQL导入数据。这导致了' hibernate_sequence'的问题。 hibernate_sequence是id 123,但我的表中有一行id大于123。

答案 2 :(得分:1)

有同样的问题-出于某种原因,休眠状态未从序列中选择正确的数字。尝试了所有运气不佳的方法,最后得出了这个解决方案:

@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.2hibernate 5.3.7pg 42.2.5

答案 3 :(得分: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;
}