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

时间:2011-02-16 16:14:43

标签: java postgresql jpa eclipselink

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

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

现在,经过大量的工作进入数据清理后,现在终于可以合并它们,只需使用其中一个字段作为鉴别器。谈论不那么抽象,表格包含公司。他们要么是当地居民,要么不是(两个班级)。可以通过邮政编码(鉴别字段)轻松区分它们。这些表是缓慢变化的维度(序列是代理键)。其他两个表包含附加到这些SCD的正常数据。因此,4桌。 2为本地公司,2为非本地公司。

现在这些表已经过简化和合并,所以我现在只有CompanyCompanyData

为了安全起见,我不会丢失任何历史信息,而是使用新的序列字段创建了两个新表。旧的序列保存在10年后,我意识到出了问题;)

到目前为止一切顺利。

重组相当容易,重新连接正确的条目也是一个明智的选择。接下来,我需要更新与此DB接口的应用程序,这是一项更多的工作,但仍然很容易。该应用程序使用JPA,使用EclipseLink 2.0 - 如上所述 - 一个PostgreSQL 9.0数据库。

这是奇怪的部分:

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

所以我做了一些挖掘。我可以验证后续的inerts确实通过递增 ID返回了重复的键错误。这意味着序列逻辑正常。唯一的问题是当前值太低。因此,调用nextval(或JPA使用的任何内容)将返回已存在的ID。

我在JPA-Entity中有以下内容:

@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...

[...]

如您所见,它尝试插入标识为19611的实体,但序列的最后一个值为19659。这显然是错误的。

我还尝试重启所有这些后面的应用程序服务器,以便关闭所有打开的连接和会话。没有运气......我注意到的另一件事:该字段被定义为Integer。应该是Long吗?这将需要在代码中进行相当多的更改,我还没有时间来解决这个问题。

由于我只有50个条目,我可以简单地尝试运行插入50次,但我更确切地知道出了什么问题......

我在这里缺少什么?

更新经过多次挖掘后,我遇到了allocationSize,其默认值为50.有趣的是,这与我看到的ID的差异非常接近。由于某些测试和毛躁,它可能不是100%相同。它有关系吗?老实说,我并不理解这个背景背后的想法......

2 个答案:

答案 0 :(得分:5)

当然对于Hibernate,如果使用GenerationType.SEQUENCE,则默认使用hi / lo策略,在数据库返回的值之前最多allocationSize个ID。将allocationSize设置为1,它应该是DTRT。

以前对非常类似问题的回答:Hibernate generating two different sequence Ids for PostgreSQL insert

答案 1 :(得分:2)

是的,这是因为你的allocationSize是50,(默认值)。我们EclipseLink确实next_value假设增量为50,那么之前的50个ID。

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

如果您希望坚持使用1,请将注释中的allocationSize更改为1.

我建议使用long来获取id,但是int最多可以保证为4,294,967,296,所以取决于你认为在应用程序的生命周期中你将有超过40亿行。