Hibernate,@SequenceGenerator 和 allocationSize

我们都知道 Hibernate 在使用 @SequenceGenerator时的默认行为-它将实际数据库序列增加 ,将该值乘以50(默认 allocationSize值)-然后使用该值作为实体 ID。

这是不正确的行为,与 规格的说法相冲突:

AllocationSize-(可选)在从序列分配序列号时增加的数量。

需要说明的是: 我不关心生成的 ID 之间的间隔。

我关心的 ID 是具有底层数据库序列的 不一致。例如: 任何其他应用程序(例如使用普通 JDBC)可能希望在从序列获得的 ID 下插入新行-但是所有这些值可能已经被 Hibernate 使用了!疯狂。

有人知道这个问题的解决方案吗(不设置 allocationSize=1,从而降低性能) ?

编辑:
把事情说清楚。 如果上次插入的记录 ID = 1,那么 HB 同时为其新实体使用值 51, 52, 53...,但是: 数据库中序列的值将被设置为 2。当其他应用程序使用该序列时,这很容易导致错误。< br/>

另一方面: 规范说(在我的理解中)数据库序列应该被设置为 51,同时 HB 应该使用范围 2, 3 ... 50的值


更新:
正如 Steve Ebersole 在下面提到的: 通过设置 hibernate.id.new_generator_mappings=true可以实现我描述的行为(也是许多人最直观的行为)。

谢谢大家。

更新2:
对于未来的读者,下面您可以找到一个工作示例。

@Entity
@Table(name = "users")
public class User {


@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USERS_SEQ")
@SequenceGenerator(name = "USERS_SEQ", sequenceName = "SEQUENCE_USERS")
private Long id;
}

Xml

<persistence-unit name="testPU">
<properties>
<property name="hibernate.id.new_generator_mappings" value="true" />
</properties>
</persistence-unit>
164348 次浏览

allocationSize=1在获取查询之前,这是一个微观优化,Hibernate 尝试在 allocationSize 范围内分配值,因此尽量避免查询数据库的顺序。但是如果将此查询设置为1,则每次都会执行此查询。这几乎没有什么区别,因为如果您的数据库被其他应用程序访问,那么如果同一个 id 被其他应用程序同时使用,就会产生问题。

下一代序列 ID 基于 allocationSize。

默认情况下,它保持为 50,这是太多。只有在一个会话中有大约 50记录,而这些记录没有被持久化,并且使用这个特定的会话和事务被持久化时,它才会有所帮助。

因此,在使用 SequenceGenerator时,应始终使用 allocationSize=1。对于大多数底层数据库序列总是增加 1

要绝对清楚... 你所描述的 没有是否与规范有任何冲突。该规范讨论的是 Hibernate 分配给实体的值,而不是实际存储在数据库序列中的值。

但是,您可以选择获得所需的行为。首先看看我的答复在 有没有一种使用 JPA 注释和 Hibernate 动态选择@GeneratedValue 策略的方法?,这将给你的基本知识。只要设置为使用 SequenceStyleGenerator,Hibernate 就会使用 SequenceStyleGenerator 中的“池式优化器”解释 allocationSize。“池优化器”用于允许在创建序列时使用“增量”选项的数据库(并非所有支持序列的数据库都支持增量)。无论如何,请阅读这里的各种优化器策略。

我将检查模式中的序列的 DDL。JPA 实现只负责创建具有正确分配大小的序列。因此,如果分配大小为50,那么序列的 DDL 必须增加50。

这种情况通常发生在创建一个分配大小为1的序列之后,然后配置为分配大小为50(或缺省值) ,但是序列 DDL 没有更新。

史蒂夫 · 埃伯索莱及其他成员,
请您解释一下为什么 id 的间隙更大(默认为50) ? 我正在使用 Hibernate 4.2.15,并在 org.Hibernate.id. 增强的. OptimizerFactory cass 中找到了以下代码。

if ( lo > maxLo ) {
lastSourceValue = callback.getNextValue();
lo = lastSourceValue.eq( 0 ) ? 1 : 0;
hi = lastSourceValue.copy().multiplyBy( maxLo+1 );
}
value = hi.copy().add( lo++ );

每当它到达 if 语句的内部时,hi 值就会变得更大。因此,在使用频繁的服务器重启进行测试期间,我的 id 会生成以下序列 id:
1,2,3,4,19,250,251,252,400,550,750,751,752,850,1100,1150.

我知道您已经说过它与规范没有冲突,但是我相信对于大多数开发人员来说这将是非常出乎意料的情况。

任何人的意见都会很有帮助。

Jihwan

更新: Ne1410s: 谢谢你的编辑。
Cfrick: 好的。我会这么做的。这是我在这里的第一篇文章,不知道怎么用。

现在,我更好地理解了为什么 maxLo 用于两个目的: 由于 hibernate 调用 DB 序列一次,保持在 Java 级别增加 id,并将其保存到 DB,Java 级别的 id 值应该考虑改变了多少,而不是在下次调用该序列时调用 DB 序列。

例如,序列 id 在某一点为1,并且 hibernate 输入5、6、7、8、9(使用 allocationSize = 5)。下一次,当我们得到下一个序列号时,DB 返回2,但是 hibernate 需要使用10,11,12... 所以,这就是为什么“ hi = lastSourceValue.copy ()。MultiplyBy (maxLo + 1)”用于从 DB 序列返回的2中获取下一个 id 10。似乎只有在频繁的服务器重启期间才会出现问题,这就是我在较大的间隙中遇到的问题。

因此,当我们使用 SEQUENSID 时,表中插入的 ID 与 DB 中的 SEQUENSnumber 不匹配。

在深入研究休眠源代码和 下面的配置将转到 Oracledb,以获得50次插入后的下一个值。因此,每次调用 INST _ PK _ SEQ 时,请使其增量为50。

Hibernate 5用于以下策略

再看看下面 Http://docs.jboss.org/hibernate/orm/5.1/userguide/html_single/hibernate_user_guide.html#identifiers-generators-sequence

@Id
@Column(name = "ID")
@GenericGenerator(name = "INST_PK_SEQ",
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
@org.hibernate.annotations.Parameter(
name = "optimizer", value = "pooled-lo"),
@org.hibernate.annotations.Parameter(
name = "initial_value", value = "1"),
@org.hibernate.annotations.Parameter(
name = "increment_size", value = "50"),
@org.hibernate.annotations.Parameter(
name = SequenceStyleGenerator.SEQUENCE_PARAM, value = "INST_PK_SEQ"),
}
)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "INST_PK_SEQ")
private Long id;

我在 Hibernate 5中也遇到过这个问题:

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = SEQUENCE)
@SequenceGenerator(name = SEQUENCE, sequenceName = SEQUENCE)
private Long titId;

下面是这样的警告:

发现使用废弃的 org.hibernate.id。SequenceHiLoGenerator ]基于序列的 id 生成器; 使用 org.hibernate.id. 增强。而是 SequenceStyleGenerator。有关详细信息,请参阅 Hibernate 域模型映射指南。

然后把我的代码改成 SequenceStyleGenerator:

@Id
@GenericGenerator(name="cmrSeq", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
@Parameter(name = "sequence_name", value = "SEQUENCE")}
)
@GeneratedValue(generator = "sequence_name")
private Long titId;

这解决了我的两个问题:

  1. 已废弃的警告已修复
  2. 现在根据 Oracle 序列生成 id。