spring / hibernate: 为 Id 列自动生成 UUID

spring / hibernate: Generate UUID automatically for Id column

我正在尝试使用 Spring、hibernate/JPA 和 PostgreSQL 数据库来坚持一个简单的 class。

table的ID列是一个UUID,我想在代码中生成,而不是在数据库中。

这应该是 straightforward,因为 hibernate 和 postgres 对 UUID 有很好的支持。

每次我创建一个新实例并用save()写入它时,我得到以下错误:

o.h.j.JdbcSQLIntegrityConstraintViolationException: NULL not allowed for column "ID"; SQL statement: INSERT INTO DOODAHS (fieldA, fieldB) VALUES , ) ...

此错误表明它期望在插入行时自动填充 ID 列(使用一些默认值)。

class 看起来像这样:

@lombok.Data
@lombok.AllArgsConstructor
@org.springframework.data.relational.core.mapping.Table("doodahs")
public class Doodah {

    @org.springframework.data.annotation.Id
    @javax.persistence.GeneratedValue(generator = "UUID")
    @org.hibernate.annotations.GenericGenerator(name="UUID", strategy = "uuid2")
    @javax.persistence.Column(nullable = false, unique = true)
    private UUID id;

    //... other fields

我尝试过的事情:

  1. @javax.persistence.Id 注释字段(除了现有的 spring Id)
  2. @org.hibernate.annotations.Type(type = "pg-uuid")
  3. 注释字段
  4. 自己创建 UUID - 结果 Spring 抱怨找不到具有该 ID 的行。
  5. 指定strategy = "org.hibernate.id.UUIDGenerator"
  6. @Entity注释class
  7. @javax.persistence.Id
  8. 替换spring@Id注解

我看到了有用的答案 here, and ,但 none 到目前为止还有效。

注意,持久性由 class 处理,如下所示:

@org.springframework.stereotype.Repository
public interface DoodahRepository extends CrudRepository<Doodah, UUID> ;

table 的 DDL 是这样的:

CREATE TABLE DOODAHS(id UUID not null, fieldA VARCHAR(10), fieldB VARCHAR(10));

更新

感谢 Sve Kamenska,在他的帮助下我终于让它运行起来了。我放弃了 JPA 方法 - 请注意,我们使用的是 R2DBC,而不是 JDBC,因此答案并没有立即奏效。多个来源 (, here, here, here, here and ) 表明 R2DBC 没有自动生成 ID。所以你必须添加一个回调 Bean 来手动设置你的 Id

我更新了 class 如下:

@Table("doodahs")
public class Doodah {

    @org.springframework.data.annotation.Id
    private UUID id;

我还添加了一个Bean如下:

@Bean
BeforeConvertCallback<Doodah> beforeConvertCallback()  {
    return (d, row, table) ->   {
        if (d.getId() == null){
            d.id = UUID.randomUUID();
        }
        return Mono.just(d);
    };
}

当一个新对象(带有 id = nullisNew = true)被传递给 save() 方法时,回调方法被调用,并设置 id。

最初我尝试使用 BeforeSaveCallback 但在过程中被调用得太迟,导致以下异常:

JdbcSQLIntegrityConstraintViolationException: NULL not allowed for column "ID"....

更新 至少有两种类型的 Spring 数据:JPAJDBC

出现此问题是因为您混合了其中的 2 个。

因此,为了修复,有 2 个解决方案。

解决方案 1 - 仅使用 Spring 数据 JDBC.

  1. Pom.xml依赖

     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-data-jdbc</artifactId>
     </dependency>
    
  2. 生成 ID。

Spring 数据 JDBC 假定 ID 是在数据库级别生成的(就像我们已经从日志中得出的那样)。如果您尝试使用 pre-defined id 保存实体,Spring 将假定它是现有实体并尝试在数据库中找到它并更新。这就是为什么您在尝试 #3 时遇到此错误。

为了生成UUID,您可以:

解决方案 2 - 仅使用 Spring 数据 JPA

  1. Pom.xml依赖

     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-data-jpa</artifactId>
     </dependency>
    
  2. 生成 ID。

实际上,您可以在这里使用带有 UUID auto-generation 的方法,就像您最初想做的那样

  • 在 class-level

    上使用 javax.persistence @Entity 注释而不是 springdata @Table
  • 使用 @javax.persistence.Id@javax.persistence.GeneratedValue 并在 id-field.

    上使用所有默认值

    @javax.persistence.Id

    @javax.persistence.GeneratedValue

    私人 UUID id;

其他说明:

  • 不需要指定生成器和策略,因为它将根据 id 字段的类型生成(在本例中为 UUID)。
  • 也不需要指定 Column(nullable = false, unique = true),因为放置 @Id 注释已经假设了这些约束。

更新前的初步回答

主要问题:如何保存实体?由于 id-generation 由 JPA 提供程序处理,在本例中为 Hibernate。它在 emrepository 的保存方法期间完成。为了创建实体和 ID,Hibernate 正在寻找 javax.persistence 注释,而你有 Spring-specific,所以我在想你如何保存它们。

还有一个问题:您提供的错误 INSERT INTO DOODAHS (fieldA, fieldB) VALUES , 显示 insert-query 中根本没有 id 字段。您是否只是简化了 error-message 并从中删除了 ID?或者这是原始错误,您的代码甚至没有“看到”字段 ID?在那种情况下,问题与 id-generation 无关,而是与为什么您的代码看不到此字段的问题有关。