Spring 数据 R2DBC PostgreSQL 未使用 UUID @Id 保存新记录

Spring Data R2DBC PostgreSQL not saving new record with UUID @Id

我有一个简单的实体,由两个 UUID 组成:

@Table("library")
public class LibraryDao {
    @Id
    private UUID id;
    @NonNull
    private UUID ownerId;
}

我在PostgreSQL中有对应的table:

CREATE TABLE IF NOT EXISTS library (id UUID PRIMARY KEY, owner_id UUID NOT NULL);

我正在使用正确的 R2DBC 驱动程序(io.r2dbc:r2dbc-postgresqlorg.postgresql:postgresql)。

至此,一切正常。我的应用程序运行。但是……

因为 PostgreSQL 没有——至少根据文档——没有自动生成 UUID 的功能,所以我在创建新的 LibraryDao 实例时设置了 id。

但是,当我在我的存储库中调用 save 方法时,出现异常:Failed to update table [library]. Row with Id [0ed4d7c0-871a-4473-8997-4c9c1ec67a00] does not exist.

似乎 save 被解释为 update,如果 insert 不存在,则不会回退到 insert

我应该如何将新记录插入我的数据库?

UUID可以是auto-generated,如果你自己生成它 Hibernate看到有id的实体并尝试更新它。

要 auto-generate uuid,只需在您的字段上使用以下注释:

@GeneratedValue(generator = "UUID")
@GenericGenerator(
    name = "UUID",
    strategy = "org.hibernate.id.UUIDGenerator",
)

来源:https://thorben-janssen.com/generate-uuids-primary-keys-hibernate/

尽管有 PostgreSQL 文档,但有一种方法可以使用 pgcrypto 扩展(对于 v4 UUID)auto-generate UUID。 (基于使用 pgAdmin GUI 的过程。)

在查询工具中:

  • select * from pg_extension 并检查 pgcrypto 是否未列出。
  • create extension pgcrypto 安装;它带有默认安装。

然后,将列定义更改为 ... id UUID PRIMARY KEY DEFAULT gen_random_uuid(), ...

而且有效。

由于 R2DBC(还)没有 auto-generation 的 UUID,并且内部使用的工具 Spring 有自己的机制来防止向 DB 发送空值(因此触发器可以完成这项工作' t 被使用) - 也许这里的最佳解决方案是使用 Java (UUID.randomUUID();) 生成 UUID,并在将实体发送到 DB 之前将它们放入实体中。

编辑 - 具体解决方案:

您的解决方案的一个小问题是必须修改数据库安装。

在意识到所描述的插入问题后,我不再尝试使用存储库 save 方法,而是使用 R2DBC DatabaseClient:

切换到“手动”解决方案
dbClient.sql("insert into product_price(id, product_id, price) values(:id, :product_id, :price)")
      .bind("id", UUID.randomUUID())
      .bind("product_id", 1)
      .bind("price", 10.0)
      .fetch()
      .one()
      .subscribe();

这里是 DatabaseClient,从 ConnectionFactory 配置并启用以接受命名参数:

@Bean
public DatabaseClient dbClient(ConnectionFactory connectionFactory) {
    return DatabaseClient.builder()
            .connectionFactory(connectionFactory)
            .namedParameters(true)
            .build();
}

ConnectionFactory 也是一个易于定制的 Bean,我在扩展 AbstractR2dbcConfiguration.

的配置 class 中公开了它

并且希望反应式存储库中的 save() 将得到改进,使其表现得与其他 spring-data 存储库中的对应物一样。

但是,除了我的 about doing inserts using R2DBC DatabaseClient, there IS a way to do it using reactive repository's save method - by using Persistable 界面,它还很漂亮 straight-forward。

它的 isNew 方法可以使用实体的 version 属性来实现,或者通过添加一个可以在调用 [=15 之前显式设置的新的专用属性(类似于 boolean isAlreadyPersisted)来实现=]. 也许还有其他一些我不知道的利用 Persistable 的方法。