0

有2个应用程序:一个是使用Spring boot - 1.5.18.Release版本,其休眠版本为5.0.12.Final

https ://search.maven.org/artifact/org.springframework.boot/spring-boot-dependencies/1.5.18.RELEASE/pom

另一个应用程序正在使用 Spring boot - 2.4.1 版本,其休眠版本为5.4.25.Final:https://search.maven.org/artifact/org.springframework.boot/spring-boot-dependencies/2.4.1 /pom 我们使用了@SequenceGenerator(name = "sequenceGenerator", sequenceName = "ABCD_SEQ",allocationSize = 1),

分配大小是必需的,因为应用程序没有启动

而第一次申请时不需要分配大小。

数据库序列是使用“INCREMENT BY 1”创建的,并且两个应用程序都使用相同的 oracle 数据库。

这两个应用程序使用了许多类似的实体,这些实体被复制到另一个应用程序/项目中。

但是当从 spring-boot 为 2.4.1 的第二个应用程序插入记录时,我们遇到了独特的序列生成器问题。

分析时,我们发现第一个应用程序(1.5.18.Release)突然增加序列,尽管它应该增加 1,在两者之间留下很多间隙,有时是 50、100 等,当第二个应用程序( 2.4.1) 尝试插入记录,出现唯一约束错误。

请帮忙,究竟在哪里搜索根本原因,或者这种情况下如何使用休眠缓存机制?

第一个应用程序中的一个实体(1.5.18.Release)

@Entity
@Table(name = "MERCURY_INSTANCE")
public class MercuryInstance implements Serializable {

    @Id
    @Column(name = "MERCURY_INSTANCE_KEY", nullable = false)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seqGen")
    @SequenceGenerator(name = "seqGen", sequenceName = "MERCURY_INSTANCE_SEQ")
    private Long mercuryInstanceKey;

    @ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
    @JoinColumn(name = "MERCURY_KEY", referencedColumnName = "MERCURY_KEY", nullable = false)
    private MERCURY mercury;

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "MERCURY_INSTANCE_KEY", referencedColumnName = "MERCURY_INSTANCE_KEY", nullable = false)
    private MercuryInstanceTechParams MercuryInstanceTechParams;

    @ManyToMany(mappedBy = "mercuryGroupInstances")
    private List<MercuryGroupInstance> MercuryGroupInstances;
    
    @Column(name = "CREATED_DATE")
    private Timestamp createdDte;
    @Column(name = "CREATED_BY")
    private String createdBy;
    @Column(name = "UPDATED_DATE")
    private Timestamp updatedDte;
    @Column(name = "UPDATED_BY")
    private String updatedBy;
    /* getter and setters of above fields */

}

虽然另一个应用程序(2.4.1)是相似的,但唯一的区别是序列生成器,例如:

@Id
@Column(name = "MERCURY_INSTANCE_KEY", nullable = false)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seqGen")
@SequenceGenerator(name = "seqGen", sequenceName = "MERCURY_INSTANCE_SEQ", allocationSize = 1)
private Long mercuryInstanceKey;

并且数据库序列是:

CREATE SEQUENCE "MERCURY_INSTANCE_SEQ" MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 55 NOCACHE NOORDER NOCYCLE ;

4

2 回答 2

1

@SequenceGenerator默认分配大小为 50。首先,最佳实践是将分配大小与INCREMENT BYdb sequence 中的值对齐。

该值往往取决于您的应用程序是读取密集型还是写入密集型。此外,您还必须考虑性能(如果它经常写入数据库,较低的值可能会导致性能问题)。如果您的应用程序是只读应用程序,则使用分配大小 1000 或 1 的影响可以忽略不计。

下一代Sequence Id 基于allocationSize。

因此,例如,第一个应用程序请求 1-50 的 id,而第二个应用程序在每次插入时都询问。在这种情况下,第一个应用程序将使用 1-50 的 id,但随后第二个应用程序请求一个 id 并将获得一个 1-50 范围内的 id(因为INCREMENT BY=1)。这会导致任何应用程序将使用相同的 id 保存第二个异常。

因此,最简单的解决方案是更改:

@SequenceGenerator(name = "seqGen", sequenceName = "MERCURY_INSTANCE_SEQ")

第一个应用程序到:

@SequenceGenerator(name = "seqGen", sequenceName = "MERCURY_INSTANCE_SEQ", allocationsize=1)

于 2021-05-18T08:22:37.630 回答
0

'allocationSize'并不意味着实体 id 会增加这个值,而是一个数字,之后将再次进行数据库查询以获得下一个数据库序列值。在应用程序方面,实体实例的 id 将始终增加 1,除非我们达到 allocationSize 限制。达到“allocationSize”后,将再次从数据库序列中检索下一个 id。如果应用程序在达到 allocationSize 限制之前重新启动或重新部署,我们将看到下一个值的一次性跳跃。'allocationSize' 是为了提高性能。

在您的第一个场景中: 序列生成器是一致的。它的唯一任务是生成唯一的整数值,仅此而已。如前所述,此行为是由 oracle 缓存、预分配、序列号(默认为 20)引起的。ID 列是代理/人工主键,仅用于唯一标识行,不应从中派生任何信息。即使您不缓存序列号,由于回滚事务、删除、应用程序和数据库服务器重新启动,您也永远不会获得不间断的一系列 ID。并且不缓存序列会对大容量事务系统造成严重的性能损失。

在您的第二种情况下: 尝试将 SequenceGenerator 放在类的顶部,以使休眠获取正确的序列。

@Entity
@Table(name = "{your_table_name}")
@SequenceGenerator(name = "seqGen", sequenceName = "MERCURY_INSTANCE_SEQ", allocationSize = 1)
public class {$your class name$} {


@Id
@Column(name = "MERCURY_INSTANCE_KEY", nullable = false)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seqGen")
private Long mercuryInstanceKey;
于 2021-05-18T08:40:51.543 回答