Java JPA 2@SequenceGenerator@GeneratedValue生成唯一约束冲突

Java JPA 2@SequenceGenerator@GeneratedValue生成唯一约束冲突,java,hibernate,postgresql,jpa,Java,Hibernate,Postgresql,Jpa,问题概述 在看似随机的时候,我们会得到一个异常“postgresql重复键违反了唯一约束”。我想我知道我们的问题是什么,但我不想在没有可复制的测试用例的情况下对代码进行更改。但由于我们除了在生产中随机复制外,还无法在任何环境中复制它,所以我请求SO的帮助 在这个项目中,我们有多个postgres数据库,并且为每个数据库中的每个表配置了一个主键序列。这些序列是这样创建的: create sequence PERSONS_SEQ; create sequence VISITS_SEQ; etc...

问题概述

在看似随机的时候,我们会得到一个异常“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;
  ...
}
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;
  ...
}
我们使用这些序列为如下实体生成主键:

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;
  ...
}
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;
  ...
}
分析

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

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

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

基于这一猜测,我认为应该将代码修改为如下内容:

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;
  ...
}
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;
  ...
}
我只是想测试一下,而不是问这样的问题,但同样,我们无法在任何其他环境中重现这个生产问题。即使在生产中,唯一的约束冲突也只在看似随机的时间发生

问题:

1) 我的分析正确吗?为了修复这个独特的约束冲突,应该进行哪些更改

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

  • 是的,你的分析是正确的。您正确地识别了问题(我们有一个类似的问题)。 而且。。。如果你要把它投入生产,别忘了:

    • 手动为新序列生成器生成具有正确初始值/初始ID的序列表(否则hibernate将从1开始,您将再次获得)
    • 或者在代码中设置该值(检查@SequenceGenerator中的initalValue
  • 我无法列举最佳实践,但我想您可以将50个限制降低。我也没有使用PostgreSQL的经验,但在MySQL中,您有一个简单的seq表。生成器和hibernate完成了全部工作


  • 我有一个类似的问题。在我的例子中,我直接通过SQL导入数据。这导致“休眠_序列”出现问题。hibernate_序列的id为123,但我的表中有一些行的id大于123。

    也有同样的问题-由于某种原因,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数据jpa2.1.2
    hibernate5.3.7
    pg42.2.5
    ,我也遇到了同样的问题。我试着解决这个问题。也许这不是最好的解决办法,但我希望它能暂时解决你的问题

    @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;
    }
    

    这应该是一个注释,而不是一个答案。它在变量上对我有效。但是,
    allocationsize=1
    是帮助我的关键点。谢谢