Spring Boot JPA-是否可以使用IDENTITY生成器或等价物批量插入?

问题描述

我最近才发现有关在Spring Boot JPA 2.2.6和MysqL 8中批量插入的信息。

要使批量插入工作正常,我必须更改以下内容

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

收件人:

@Id
@GeneratedValue(generator = "generator")
@GenericGenerator(name = "generator",strategy = "increment")
@Column(name = "id",nullable = false)
private long id;

具有一些其他属性

spring:
  jpa:
    properties:
      hibernate:
        generate_statistics: true
        jdbc:
          batch_size: 1000
        order_inserts: true
        order_updates: true

当我使用单个服务器实例进行测试时,批量插入似乎可以正常工作。但是我刚刚注意到,当有两个API实例时,id递增无法正常工作。

似乎正在发生的事情:

  1. POST以创建实体并将其持久保存在MysqL数据库中。实例1接收到该请求。该实体的ID为1。
  2. POST以创建实体并将其持久保存在MysqL数据库中。该请求由实例2接收(Round Robin请求路由)。引发以下异常:
    Aug 10 20:47:00 ip-10-1-0-120.dev.dev.uk bash[25107]: 2020-08-10 20:47:00.408  INFO 25107 --- [nio-8080-exec-4] o.h.e.j.b.internal.AbstractBatchImpl     : HHH000010: On release of batch it still contained JDBC statements
    Aug 10 20:47:00 ip-10-1-0-120.dev.dev.uk bash[25107]: 2020-08-10 20:47:00.410 ERROR 25107 --- [nio-8080-exec-4] o.h.e.jdbc.batch.internal.BatchingBatch  : HHH000315: Exception executing batch [java.sql.BatchUpdateException: Duplicate entry '1' for key 'PRIMARY'],sql: insert into REMINDER (deleted,created,created_by,last_modified,last_modified_by,context,notification_type,reminder_date,user_id,id) values (?,?,?)
    Aug 10 20:47:00 ip-10-1-0-120.dev.dev.uk bash[25107]: 2020-08-10 20:47:00.411  WARN 25107 --- [nio-8080-exec-4] o.h.engine.jdbc.spi.sqlExceptionHelper   : sql Error: 1062,sqlState: 23000
    Aug 10 20:47:00 ip-10-1-0-120.dev.dev.uk bash[25107]: 2020-08-10 20:47:00.412 ERROR 25107 --- [nio-8080-exec-4] o.h.engine.jdbc.spi.sqlExceptionHelper   : Duplicate entry '1' for key 'PRIMARY'

当我要使用批量插入时,如何防止在使用多个实例时抛出重复的键异常,又要确保每个新实体始终在多个实例中获取一个可用的递增ID?

解决方法

GenerationType.IDENTITY不允许批量插入。您可以看到此内容,例如here

实际上,当使用多个实例时,此代码将不起作用。因为策略increment使用IncrementGenerator实现。在那里,您可以看到缓存了数据库的最大ID值。因此,如果应用程序的另一个实例插入了一个值,那么当前实例将永远不会知道它。

要解决您的问题,您可以使用

    @Id
    @GeneratedValue
    private UUID id;

或者您应该将数据库更改为PostgreSQL,然后必须使用sequence策略。

如果这些选项不适合您,您可以在DefaultIdentifierGeneratorFactory

中查看用于生成标识符的其他策略的实现