代码之家  ›  专栏  ›  技术社区  ›  Ranjithkumar

Hibernate GenerationType.IDENTITY未生成序列ID

  •  0
  • Ranjithkumar  · 技术社区  · 3 年前

    在我的springboot应用程序中,我在插入新行时注意到一个奇怪的问题。

    我的id是按顺序生成的,但在我重新启动应用程序后,它从21开始。

    示例:

    第一次启动,我插入3行-序列1,2,3生成的ID

    在重新启动第二次启动后,我插入了从21生成的3行id。所以id是21,22。。。

    每次重新启动它都增加到20- 这种增长模式总是20

    请参阅我的数据库表(重启后1,2 21)

    enter image description here

    我的JPA实体

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(unique = true, nullable = false)
    private Long id;
    

    我尝试了一些堆叠式气流解决方案,但不起作用

    我试过了,没用

    spring.jpa.properties.hibernate.id.new_generator_mappings=false
    

    我想按顺序插入行,如1,2,3,4。不是这样的1,2,21,22,如何解决这个问题?

    0 回复  |  直到 3 年前
        1
  •  3
  •   jccampanero    3 年前

    尽管我认为问题评论已经提供了理解问题所需的所有信息,但请允许我尝试解释一些事情,并尝试纠正一些不准确的地方。

    根据您的源代码,您正在使用 IDENTITY 身份生成策略:

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(unique = true, nullable = false)
    private Long id;
    

    您使用的是Oracle数据库,这是一个与问题非常相关的信息。

    支持 身份 列是在Oracle12c(可能是Release 1)和Hibernate版本中引入的——我想说 5.1 虽然 here in SO 表示您至少需要-5.3。

    不管怎样, 身份 数据库的使用支持Oracle中的列 SEQUENCE s: 即,对于 身份 列中创建相应的序列。正如您在 Oracle documentation 这解释了为什么创建序列的所有选项都可以应用于 身份 列定义,如最小和最大范围、缓存大小等。

    默认情况下,Oracle中的序列的缓存大小为 20 如中的一个小注释所示 aforementioned Oracle documentation :

    注: 创建标识列时,Oracle建议您 指定 CACHE 子句的值高于的默认值 20 到 提高性能。

    这个默认的缓存大小就是为什么您在 id 价值观

    这种行为并不是Hibernate独有的:请使用任何合适的工具发出一个简单的JDBC插入语句或SQL命令,您就会进行同样的实验。

    要解决此问题,请创建表格,指示 NOCACHE 对于您的IDENTITY列:

    CREATE TABLE your_table (
      id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY NOCACHE,
      --...
    )
    

    注意您需要使用 NOCACHE 而不是 CACHE 0 如问题注释中所示,现在是其他答案的前一版本,这是一个错误,因为 CACHE option 应该至少 2

    也许您可以在不重新创建整个表的情况下修改列:

    ALTER TABLE your_table MODIFY (ID GENERATED BY DEFAULT ON NULL AS IDENTITY NOCACHE);
    

    说了这么多,请注意,事实上 cache mechanism 是一种优化,而不是缺点:最终,这只是我的观点,这些ID只是非自然分配的ID,在一般用例中,缓存的好处大于缺点。

    请考虑阅读 this great article 关于 身份 列。

    提供的答案与 hilo 优化器可能是正确的,但它需要在 身份证 字段声明,但情况似乎并非如此。

        2
  •  2
  •   diziaq    3 年前

    它与Hibernate用于递增序列值的Hi/Lo算法有关。阅读此示例中的更多信息: https://www.baeldung.com/hi-lo-algorithm-hibernate

    这是Hibernate使用的优化,它将DB序列中的一些值消耗到池(Java运行时)中,并在执行适当的值时使用它们 INSERT 表上的语句。如果关闭并设置了此优化 allocationSize=1 ,则所需的行为(id中没有间隙)是可能的(具有一定的精度,并非总是如此),但代价是为每个请求向DB发出两个请求 插入

    示例给出了上层抽象中正在发生的事情的概念。 (内部实现更复杂,但在这里我们不在乎)

    场景: 用户在一段时间内插入21个

    示例1(当前行为 allocationSize=20 )

       #1 insert: // first cycle 
       - need next MY_SEQ value, but MY_SEQ_PREFETCH_POOL is empty
       - select 20 values from MY_SEQ into MY_SEQ_PREFETCH_POOL   // call DB
       - take it from MY_SEQ_PREFETCH_POOL >> remaining=20-1
       - execute INSERT                                           // call DB
    
       #2-#20 insert:
       - need next MY_SEQ value, 
       - take it from MY_SEQ_PREFETCH_POOL >> remaining=20-i
       - execute INSERT                                           // call DB
     
       #21 insert: // new cycle 
       - need next MY_SEQ value, but MY_SEQ_PREFETCH_POOL is empty
       - select 20 values from MY_SEQ into MY_SEQ_PREFETCH_POOL   // call DB
       - take it from MY_SEQ_PREFETCH_POOL >> remaining=19
       - execute INSERT                                           // call DB
    

    示例2(当前行为 分配大小=1 )

       #1-21 insert:
       - need next MY_SEQ value, but MY_SEQ_PREFETCH_POOL is empty
       - select 1 value from MY_SEQ into MY_SEQ_PREFETCH_POOL     // call DB
       - take it from MY_SEQ_PREFETCH_POOL >> remaining=0
       - execute INSERT                                           // call DB
    

    示例#1 :对数据库的调用总数为 23
    示例#2 :对数据库的调用总数为 42

    在这种情况下,在数据库中手动声明序列将没有帮助,因为,例如在以下语句中\

    CREATE SEQUENCE ABC START WITH 1 INCREMENT BY 1 CYCLE NOCACHE;

    我们只控制DB内部运行时中使用的“缓存”,这对Hibernate来说是不可见的。当DB停止并再次启动时,它会影响序列间隙,但事实并非如此。

    当Hibernate使用序列中的值时,这意味着序列的状态在DB端发生了更改。我们可以将其视为酒店房间预订:一家公司(Hibernate)为一家酒店(DB)的会议预订了20个房间,但只有2名与会者到达。然后18间客房将空置,其他客人无法使用。在这种情况下,“预订期”是永远的。

    关于如何配置Hibernate使用序列的更多详细信息,请点击此处: https://ntsim.uk/posts/how-to-use-hibernate-identifier-sequence-generators-properly

    以下是旧版本Hibernate的简短答案。尽管如此,它还是有相关的想法: https://stackoverflow.com/a/5346701/2774914