无法在 sqlite 中使用 UUID id 保存聚合根

Unable to save aggregate root with UUID id in sqlite

在我的项目中,我有以下可以成功保存在 Postgres 中的实体:

public class Aggregate {

  @Id
  private UUID id;

  private JsonNode data;

  // getters and setters omitted
}

当我尝试在 SQLite 中保存相同的实体时,出现以下异常:

org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.Integer] to type [java.util.UUID]
    at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:322) ~[spring-core-5.3.5.jar:5.3.5]
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:195) ~[spring-core-5.3.5.jar:5.3.5]
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:175) ~[spring-core-5.3.5.jar:5.3.5]
    at org.springframework.data.mapping.model.ConvertingPropertyAccessor.convertIfNecessary(ConvertingPropertyAccessor.java:120) ~[spring-data-commons-2.4.6.jar:2.4.6]
    at org.springframework.data.mapping.model.ConvertingPropertyAccessor.setProperty(ConvertingPropertyAccessor.java:63) ~[spring-data-commons-2.4.6.jar:2.4.6]
    at org.springframework.data.jdbc.core.JdbcAggregateChangeExecutionContext.setIdAndCascadingProperties(JdbcAggregateChangeExecutionContext.java:337) ~[spring-data-jdbc-2.1.6.jar:2.1.6]
    at org.springframework.data.jdbc.core.JdbcAggregateChangeExecutionContext.populateIdsIfNecessary(JdbcAggregateChangeExecutionContext.java:305) ~[spring-data-jdbc-2.1.6.jar:2.1.6]
    at org.springframework.data.jdbc.core.AggregateChangeExecutor.execute(AggregateChangeExecutor.java:52) ~[spring-data-jdbc-2.1.6.jar:2.1.6]
    at org.springframework.data.jdbc.core.JdbcAggregateTemplate.store(JdbcAggregateTemplate.java:339) ~[spring-data-jdbc-2.1.6.jar:2.1.6]
    at org.springframework.data.jdbc.core.JdbcAggregateTemplate.save(JdbcAggregateTemplate.java:149) ~[spring-data-jdbc-2.1.6.jar:2.1.6]
    at org.springframework.data.jdbc.repository.support.SimpleJdbcRepository.save(SimpleJdbcRepository.java:60) ~[spring-data-jdbc-2.1.6.jar:2.1.6]

Table SQLite 的定义:

create table aggregate
(
    id   text primary key,
    data text not null
);

出于某种原因 Spring 数据 JDBC 尝试在聚合中设置整数类型的 id,尽管它 a) 是另一种类型并且 b) 已经存在。我尝试了不同的 IdGenerator,在 table 定义中使用 without rowid 但没有任何改变。

我正在使用 Spring 数据 JDBC 2.1.6 和 xerial/sqlite-jdbc 3.34.0。

我该如何解决这个问题?


更新以回答 Jens Schauder 的问题:我忘了提及适当设置 UUID 的回调。我还有两个转换器可以在 JsonNodePGobject 之间进行转换(此处未显示)。

@Component
public class AggregateCallback implements BeforeConvertCallback<Aggregate> {

  @Override
  public Aggregate onBeforeConvert(final Aggregate aggregate) {
    if (aggregate.getId() == null) {
      aggregate.setId(UUID.randomUUID());
    }
    return aggregate;
  }
}

无论是哪个数据库,都会调用 AggregateCallback,但使用 SQLite 时会抛出异常 执行回调后。

按照 Jens Schauder 在评论中的建议切换到 Spring Data 2021.0.0-M5 解决了问题!

正如@tammOr 在 Spring Data 2021.0.0-M5 修复问题后使用版本回答的那样。 以下是原因和方式的解释。

当 Spring 数据 JDBC 保存聚合时,它在旧版本中执行以下操作:

  1. 判断聚合是否是新的。有多种方法可以做到这一点,但这里适用的情况是检查 id,如果它是空的(null 对于对象类型或 0 对于数字基元。

  2. 因为 id 是 null,它决定聚合是新的并且需要 INSERT。在执行之前,它会触发@tammOr 用来设置 id 的一些事件。

  3. 然后执行插入

  4. 由于默认策略是在数据库中生成id,插入后会尝试从JDBC驱动中获取生成的id。出于某种原因,SQLite 驱动程序实际上 returns 一个 Integer 类型的值。

  5. Spring 数据 JDBC 然后尝试将其转换为 UUID 但失败了。

使用更高版本Spring 数据JDBC 意识到它在步骤 4 中已经有一个 ID,并且不会向驱动程序询问生成的 ID,并跳过步骤 5,所以一切都很开心。

这是解决问题的拉取请求:https://github.com/spring-projects/spring-data-jdbc/pull/939