【问题标题】:Weird id values in Postgresql with JPA 2 Sequence generation带有 JPA 2 序列生成的 Postgresql 中奇怪的 id 值
【发布时间】:2011-09-15 15:57:09
【问题描述】:

实体在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 不使用序列提供的值。如果我理解正确的组合
分配大小=10 在注释和 增加 10 在序列中应该保证休眠只需要每隔十个值从序列中请求一个新值。为什么这没有发生?为什么序列中的值要乘以 10?

我正在使用

  • PostgreSQL 9.0.3
  • 休眠 3.5.5
  • 休眠 JPA 2.0 api 1.0.0 最终版

更新 1:

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

但是:

  • allocationSize=1 会导致性能问题吗?
  • 序列中的值不是在休眠状态下使用而是乘以 allocationSize 值,这不是一个巨大的错误吗?
  • 该怪谁?休眠?
  • 是否有可用的修复程序?

【问题讨论】:

标签: hibernate postgresql jpa-2.0 sequence


【解决方案1】:

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

【讨论】:

  • 是的,hibernate 绝对应该期望 db 的值是正确的。但是 allocationSize 的文档说:“从序列中分配序列号时要增加的数量。”它没有说明乘法。将allocationSize 100 与INCREMENT 100 一起使用应该使hibernate 的行为如此,它只需请求一次序列值,然后它就可以安全地将id 值分配给下一个100 记录。至少我是这么理解的。
  • 我知道这可能有点啰嗦,但是您是否对 hibernate 在创建每个新对象时发送查询以获取下一个序列号有疑问?这就是我的代码当前的工作方式...对于基准测试:在单独的查询中获取 ~650000 个序列号大约需要 1 分钟。
  • 很高兴知道这一点。所以这种“优化”很可能是完全没有必要的。
【解决方案2】:

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

引自手册:

可能会得到意想不到的结果 如果缓存设置大于一 用于将 多人同时使用 会议。每个会话将分配 并缓存连续的序列值 在一次访问序列期间 对象并增加序列 对象的 last_value 相应地。然后, nextval 的下一个 cache-1 使用 在该会话中只需返回 预先分配的值而不涉及 序列对象。所以,任何数字 已分配但未在 a 内使用 当该会话时会话将丢失 结束,导致“孔”在 序列

确保您阅读了手册中的“注意事项”部分: http://www.postgresql.org/docs/current/static/sql-createsequence.html

【讨论】:

  • 缓存设置在我的序列中实际上没有使用
  • 他们在手册中提到的意外结果是关于生成的 id 值的差距。这绝不是一个问题。重叠的 id 值会是,但使用 CACHE>1 不会导致这种情况。
【解决方案3】:

这就是它的工作原理。

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

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

【讨论】:

  • allocationSize 的文档说:“从序列中分配序列号时要增加的数量。”它没有说明乘法。但这不是这里的问题。问题是休眠或我最终会尝试向该表插入具有冲突 id 值的行。这不可能是它应该如何工作的。以及更改 db 序列的 INCREMENT 值有什么帮助?
  • @kosoant:如果你想从一个序列值产生多个标识符,你必须使用乘法,你明白吗?
  • 不,我不明白为什么必须使用乘法来获取多个 id 值从单个 nextval('sequence') 查询使用单个值,你必须使用乘法,但这不是我问题的重点。从我自己对这个问题的回答中可以看出,基于非乘法的“从单个序列查询中生成多个标识符”实际上是可能的
【解决方案4】:

似乎正确的方法如下:

@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

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

所以,关键点是:

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

如果你想优化 db inserts 的性能,那么使用

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

为了获得良好的顺序 id 值

  • 您必须使用 INCREMENT 1

【讨论】:

  • 谢谢,将allocationSize 更改为 1 对我有帮助!在 id 乘以 50 之前。大声笑
  • 这是不对的。使用 allocationSize=1,您每次都会访问数据库。您需要的是 hibernate.id.new_generator_mappings=true 。见stackoverflow.com/questions/12745751/…
猜你喜欢
  • 2017-09-21
  • 1970-01-01
  • 2014-05-12
  • 1970-01-01
  • 2016-10-21
  • 1970-01-01
  • 2017-06-03
  • 1970-01-01
  • 2011-04-04
相关资源
最近更新 更多