关于java:Hibernate,@ SequenceGenerator和allocationSize

Hibernate, @SequenceGenerator and allocationSize

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

这是错误的行为,并与说明以下内容的规范冲突:

allocationSize - (Optional) The amount to increment by when allocating sequence numbers from the sequence.

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

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

有人知道解决此问题的任何方法(不设置allocationSize=1从而降低性能)吗?

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

另一方面:规范说(据我所知),数据库序列应已设置为51,与此同时HB应使用范围2, 3 ... 50的值


更新:
如下所述,史蒂夫·埃伯索尔(Steve Ebersole):可以通过设置hibernate.id.new_generator_mappings=true启用我描述的行为(也是许多人中最直观的行为)。

谢谢大家。

更新2:
对于将来的读者,您可以在下面找到一个有效的示例。

1
2
3
4
5
6
7
8
9
@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

1
2
3
4
5
<persistence-unit name="testPU">
  <properties>
    <property name="hibernate.id.new_generator_mappings" value="true" />
  </properties>
</persistence-unit>


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

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


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

序列ID的下一代基于分配大小。

通过默认,它被保留为50,这太多了。如果您将在一个会话中保留大约50个记录,这些记录将不会持久保存,并且使用此特定会话和事务处理将被持久保存,那么它也将有所帮助。

因此,在使用SequenceGenerator时应始终使用allocationSize=1。对于大多数基础数据库,序列始终以1递增。


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

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

收到如下警告:

Found use of deprecated [org.hibernate.id.SequenceHiLoGenerator] sequence-based id generator; use org.hibernate.id.enhanced.SequenceStyleGenerator instead. See Hibernate Domain Model Mapping Guide for details.

然后将我的代码更改为SequenceStyleGenerator:

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

这解决了我的两个问题:

  • 已弃用的警告已修复
  • 现在,根据oracle序列生成了id。

  • 深入研究休眠源代码和
    在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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @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;


    史蒂夫·埃伯索尔(Steve Ebersole)和其他成员,
    您能否解释一下ID差距较大(默认值为50)的原因?
    我正在使用Hibernate 4.2.15,并在org.hibernate.id.enhanced.OptimizerFactory cass中找到以下代码。

    1
    2
    3
    4
    5
    6
    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:感谢您的编辑。
    cfrick:好的。我去做。这是我在这里的第一篇文章,不确定如何使用。

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

    例如,序列ID在某个点为1,然后休眠输入5、6、7、8、9(分配大小= 5)。下次,当我们得到下一个序列号时,DB返回2,但是休眠状态需要使用10、11、12 ...因此,这就是为什么" hi = lastSourceValue.copy()。multiplyBy(maxLo + 1)"是用于从数据库序列返回的2中获取下一个ID 10。似乎只有麻烦的事情是频繁的服务器重启期间,这是我的问题所在,而且间隔更大。

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


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

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