数据库和 JPA 中的 PostgreSQL 序列 ID 不同

问题描述 投票:0回答:2

我真的很困惑......但首先,让我给你一个粗略的概述。

我在数据库中进行了一些重组,将 4 个表合并为两个表。所有表都以简单的数字序列作为主键。事实上,这些桌子成对地非常(非常)相似。它们被分成两部分的唯一原因是基于必须导入的历史数据。如果没有这种拆分,就会出现大量冗余,从概念上讲这是有道理的。

现在,经过大量数据清理工作后,现在终于可以合并它们并简单地使用其中一个字段作为鉴别器。说得更抽象一些,这些表包含公司。他们要么是当地居民,要么不是当地居民(两个阶层)。可以通过邮政编码(鉴别器字段)轻松区分它们。这些表的维度正在缓慢变化(序列是代理键)。另外两个表包含附加到这些 SCD 的普通数据。因此,有 4 张桌子。本地公司2个,非本地公司2个。

这些表格现在已被简化和合并,所以我现在只有

Company
CompanyData

为了安全起见,并且不丢失任何历史信息,我创建了两个带有新序列字段的新表。保留旧序列,以防 10 年后我意识到出了问题;)

到目前为止一切顺利。

重组相当容易,重新连接正确的条目也很容易。接下来,我需要更新与该数据库接口的应用程序,这需要更多工作,但仍然很容易。该应用程序使用 JPA,并在 PostgreSQL 9.0 数据库之上使用 EclipseLink 2.0。

奇怪的部分来了:

当我尝试插入新公司时,出现重复键错误,指出给定的 ID 已存在。但这应该由序列对象处理......不是吗?

所以我做了一些挖掘。我可以验证后续惰性确实返回了具有incrementing id 的重复键错误。这意味着顺序逻辑是正确的。唯一的问题是当前值太低。因此,对 nextval(或 JPA 使用的任何内容)的调用将返回一个已经存在的 ID。

我在 JPA 实体中有以下内容:

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "enterprise_id_seq")
@Column(name = "id", nullable = false)
private Integer id;

我的序列如下所示:

test_db=# \d enterprise_id_seq 
      Sequence "public.enterprise_id_seq"
    Column     |  Type   |        Value        
---------------+---------+---------------------
 sequence_name | name    | enterprise_id_seq
 last_value    | bigint  | 19659
 start_value   | bigint  | 1
 increment_by  | bigint  | 1
 max_value     | bigint  | 9223372036854775807
 min_value     | bigint  | 1
 cache_value   | bigint  | 1
 log_cnt       | bigint  | 32
 is_cycled     | boolean | f
 is_called     | boolean | t

我得到的错误是这些:

[...]

Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.0.1.v20100213-
r6600): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "enterprise_pkey"
    Detail: Key (id)=(19611) already exists.
Error Code: 0
Call: INSERT INTO en...

[...]

如您所见,它尝试插入 id 为

19611
的实体,但序列上的最后一个值是
19659
。这显然是错误的。

我还尝试重新启动所有这一切背后的应用程序服务器,以便关闭所有打开的连接和会话。不走运...我注意到的另一件事:该字段被定义为

Integer
。应该是
Long
吗?这需要对代码进行相当多的更改,而我还没有时间解决这个问题。

由于我仅落后 50 个条目,我可以简单地尝试运行插入 50 次,但我宁愿知道到底出了什么问题......

我在这里缺少什么?

更新:经过更多挖掘,我发现了allocationSize,它的默认值为 50。有趣的是,这与我看到的 ID 的差异非常接近。由于一些测试和错误处理,它可能不是 100% 相同。可能有关系吗?老实说,我还没有理解这个设置背后的想法......

java postgresql jpa eclipselink
2个回答
5
投票

当然,对于 Hibernate,如果使用 GenerationType.SEQUENCE,则默认使用 hi/lo 策略,最多

allocationSize
id 位于从数据库返回的值之前。将 AllocationSize 设置为 1,它应该做正确的事情。

之前对一个非常相似的问题的回答:Hibernate生成两个不同的序列Ids for PostgreSQL插入


2
投票

是的,这是因为您的allocationSize 是50(默认值)。 我们 EclipseLink 的 next_value 假设增量为 50,因此之前的 50 个 id 也是如此。

allocationSize 必须与您的序列增量匹配。 我建议您将序列增量更新为 50,这将允许序列预分配,从而大大提高您的性能。

如果您想坚持使用 1,请将注释中的 AllocationSize 更改为 1。

我建议使用 long 作为 id,但 int 最大可达 4,294,967,296,因此取决于您是否认为应用程序的生命周期中将拥有超过 40 亿行。

© www.soinside.com 2019 - 2024. All rights reserved.