Hibernate,@ SequenceGenerator和allocationSize

时间:2012-10-05 11:55:35

标签: java hibernate jpa hilo

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

这是不正确的行为并与specification冲突,其中包含:

  

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

要明确:我不打算生成ID之间的差距。

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

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

修改
说清楚。 如果最后插入的记录具有ID = 1,则HB同时对其新实体使用值51, 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;
}

的persistence.xml

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

6 个答案:

答案 0 :(得分:34)

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

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

答案 1 :(得分:12)

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

下一代Sequence Id基于allocationSize。

通过defualt保持为50太多了。如果您在一个会话中有大约50个记录,这些记录不会持续存在,并且将使用此特定会话和转换来保留,那么它也会有所帮助。

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

答案 2 :(得分:1)

Steve Ebersole&amp;其他成员,
你能否解释一个有更大差距的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。

我知道你已经说它与规范没有冲突,但我相信这对大多数开发者来说都是非常意外的情况。

任何人的输入都会有所帮助。

Jihwan

更新: 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 ...所以,这就是为什么&#34; hi = lastSourceValue.copy()。multiplyBy(maxLo + 1) )&#34;用于从DB序列返回的2中获取下一个id 10。似乎只是困扰的事情是在频繁的服务器重启期间,这是我的问题与较大的差距。

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

答案 3 :(得分:1)

深入挖掘hibernate源代码后 下面的配置在50次插入后转到Oracle db以获取下一个值。因此,每次调用INST_PK_SEQ时都要增加50。

Hibernate 5用于以下策略

另请参见下文 rank

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

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

我在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。