138

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

这是不正确的行为,并且与说明的规范冲突:

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

需要明确的是:我不关心生成的 ID 之间的差距。

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

有人知道这个问题的任何解决方案(没有设置allocationSize=1并因此降低性能)吗?

编辑:
说清楚。如果最后插入的记录 ID = 1,则 HB51, 52, 53...同时使用其新实体的值:数据库中的序列值将设置为2。当其他应用程序使用该序列时,这很容易导致错误。

另一方面:规范说(在我的理解中)数据库序列应该设置为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>
4

6 回答 6

55

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

但是,可以选择获取您正在寻找的行为。首先查看我关于Is there a way to dynamic selection a @GeneratedValue strategy using JPA annotations and Hibernate? 这将为您提供基础知识。只要您设置为使用该 SequenceStyleGenerator,Hibernate 就会allocationSize使用 SequenceStyleGenerator 中的“池化优化器”进行解释。“池化优化器”用于允许在创建序列时使用“增量”选项的数据库(并非所有支持序列的数据库都支持增量)。无论如何,请阅读那里的各种优化器策略。

于 2012-10-05T13:35:31.900 回答
24

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

下一代Sequence Id 基于allocationSize。

默认情况下,它被保留为50太多。如果您将50在一个会话中拥有未持久化的记录并且将使用此特定会话和事务进行持久化,它也只会有所帮助。

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

于 2012-10-05T12:04:09.050 回答
2

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

这种情况通常会在创建分配大小为 1 的序列时发生,然后稍后将其配置为分配大小 50(或默认值),但序列 DDL 未更新。

于 2012-10-05T19:13:42.947 回答
1

Steve Ebersole 和其他成员,
您能解释一下为什么 id 的差距更大(默认为 50)吗?我正在使用 Hibernate 4.2.15 并在 org.hibernate.id.enhanced.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。

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

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

志焕

更新:ne1410s:感谢您的编辑。
克里克:好的。我去做。这是我在这里的第一篇文章,不知道如何使用它。

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

例如,序列 id 在某一点为 1,而休眠输入 5、6、7、8、9(allocationSize = 5)。下一次,当我们得到下一个序列号时,DB返回2,但是hibernate需要使用10、11、12......所以,这就是为什么“hi = lastSourceValue.copy().multiplyBy(maxLo+1)”是用于从 DB 序列返回的 2 中获取下一个 id 10。似乎唯一令人烦恼的事情是在频繁的服务器重启期间,这是我与较大差距的问题。

因此,当我们使用 SEQUENCE ID 时,表中插入的 id 将与 DB 中的 SEQUENCE 编号不匹配。

于 2014-09-28T19:46:39.080 回答
1

在深入研究休眠源代码和下面的配置后,在 50 次插入后转到 Oracle db 以获得下一个值。因此,每次调用 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;
于 2016-03-03T22:52:17.707 回答
1

我在 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.enhanced.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。
于 2019-05-01T12:57:31.980 回答