具有JPA 2序列生成的Postgresql中的奇怪id值

时间:2011-06-15 15:37:07

标签: hibernate postgresql jpa-2.0 sequence

实体在id列上有以下注释:

@Id
@SequenceGenerator(name = "JOB_MISFIRE_ID_GENERATOR", sequenceName="job_misfire_sequence", allocationSize=10)
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator = "JOB_MISFIRE_ID_GENERATOR")
@Column(unique = true, nullable = false)
private Long id;

在数据库中,我有以下内容:

CREATE SEQUENCE job_misfire_sequence
  INCREMENT 10
  MINVALUE 1
  MAXVALUE 9223372036854775807
  START 1
  CACHE 1;

序列用于获取列的默认值。

ALTER TABLE job_misfires
ALTER COLUMN id SET DEFAULT nextval('job_misfire_sequence');

当我使用nextval('job_misfire_sequence')手动插入数据库时​​,一切正常。当序列的当前值为1时,产生以下id值:

 SELECT nextval('job_misfire_sequence'); --> 1
 SELECT nextval('job_misfire_sequence'); --> 11
 SELECT nextval('job_misfire_sequence'); --> 21
 SELECT nextval('job_misfire_sequence'); --> 31

但是当hibernate在这个表中插入一行时会发生什么呢?它得到了这个序列的下一个值(在这个场景中是41)并将它乘以10并将其用作id值。这意味着插入的行现在具有id值410。

我做错了什么?这种情况会导致冲突,因为hibernate没有使用序列提供的值。如果我理解了相关的组合     allocationSize = 10 在注释和     增量10 在序列中应该保证hibernate只需要每十个值从序列中请求一个新值。为什么这不会发生?为什么序列中的值乘以10?

我正在使用

  • Postgresql 9.0.3
  • Hibernate 3.5.5
  • Hibernate JPA 2.0 api 1.0.0 final

更新1:

正如互联网周围建议的那样,在注释中将allocationSize值设置为1可以解决此问题。现在id值实际上取自db中的序列,我可以安全地在该表中手动插入行。

可是:

  • allocateSize = 1会导致性能问题吗?
  • 序列中的值不是在休眠状态下使用但是乘以allocationSize值,这不是一个巨大的错误吗?
  • 谁应该受到指责?休眠?
  • 有修复方法吗?

4 个答案:

答案 0 :(得分:7)

执行此操作的正确方法似乎如下:

@Id
@SequenceGenerator(name = "JOB_MISFIRE_ID_GENERATOR", sequenceName="job_misfire_sequence", allocationSize=1)
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator = "JOB_MISFIRE_ID_GENERATOR")
@Column(unique = true, nullable = false)
private Long id;

在数据库中,我有以下内容:

CREATE SEQUENCE job_misfire_sequence
  INCREMENT 1
  MINVALUE 1
  MAXVALUE 9223372036854775807
  START 1
  CACHE 10;

现在,当我使用nextval('job_misfire_sequence')手动插入数据库时​​,一切都按预期工作。当序列的当前值为1时,产生以下id值:

 SELECT nextval('job_misfire_sequence'); --> 1
 SELECT nextval('job_misfire_sequence'); --> 2
 SELECT nextval('job_misfire_sequence'); --> 3
 SELECT nextval('job_misfire_sequence'); --> 4

现在,hibernate也可以按照我的预期运行。在我在一个会话中插入这4行后插入一行时,返回到hibernate的序列值为11. Hibernate使用它作为第一条记录的id值,11作为下一条记录的id值,依此类推。因为我在db中将CACHE设置设置为10,所以hibernate现在只需要调用一次序列,然后可以使用10个连续的id值。我确认情况确实如此,并且id值不重叠。

所以,关键点是:

  • 您必须在注释中使用allocationSize = 1

如果要优化数据库插入的性能,请使用

  • 在db中使用CACHE设置,其值大于1,但不要触及allocationSize

获得不错的顺序id值

  • 您必须使用INCREMENT 1

答案 1 :(得分:0)

我理解它的方式,nextVal('job_misfire_sequence');将返回下一个SEQUENCE值,因此您需要的值。 Hibernate可以从中抽象出来,并假设从DB返回的值是正确的。因此,你不需要 allocationSize=10,因为数据库已经返回了正确的值。

答案 2 :(得分:0)

我不知道Hibernate,但是当在PostgreSQL中使用缓存值创建序列时,缓存在每个连接的基础上运行。这意味着如果从不同的会话(=连接)调用nextval(),您可能也会看到此行为。

手册中的引用:

  

可能会获得意外结果   如果缓存设置大于1   用于序列对象   由多个同时使用   会话。每个会话都将分配   并缓存连续的序列值   在一次访问序列期间   对象并增加序列   对象的last_value。然后,   nextval的下一个cache-1用法   在那个会话中只需返回   未分配的预分配值   序列对象。所以,任何数字   分配但未在a。中使用   会话将在该会话中丢失   结束,导致“洞”   序列

请务必阅读手册中的“注释”部分: http://www.postgresql.org/docs/current/static/sql-createsequence.html

答案 3 :(得分:0)

这就是它的工作原理。

当使用带有allocationSize的序列生成器时,Hibernate从序列中获取单个数字以生成allocationSize个标识符。因此,在从序列中获得值N后,它会生成从allocationSize * NallocationSize * (N + 1) - 1的标识符。然后它从序列中获取下一个值以生成下一组标识符。如果下一个值是N + 1,则生成的标识符是连续的,因此它期望产生后续数字的seqeunce。

因此,无需在序列定义中指定increment