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

问题描述

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

find . -name "*.py" | xargs etags+

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

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]

出于某种原因,Spring Data JDBC 尝试在聚合中设置整数类型的 id,尽管它 a) 是另一种类型并且 b) 已经存在。我尝试了不同的 create table aggregate ( id text primary key,data text not null ); ,在表定义中使用 IdGenerator,但没有任何改变。

我使用的是 Spring Data JDBC 2.1.6 和 xerial/sqlite-jdbc 3.34.0。

我该如何解决这个问题?


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

PGobject

无论调用的是哪个数据库@Component public class AggregateCallback implements BeforeConvertCallback<Aggregate> { @Override public Aggregate onBeforeConvert(final Aggregate aggregate) { if (aggregate.getId() == null) { aggregate.setId(UUID.randomUUID()); } return aggregate; } } 都会被调用,但是对于 sqlite,在执行回调后 会抛出异常。

解决方法

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

,

正如@tammOr 在 Spring Data 2021.0.0-M5 解决问题后使用版本回答的那样。 以下是原因和方法的说明。

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

  1. 它确定聚合是否是新的。有多种方法可以做到这一点,但这里适用的情况是检查 id,如果它为空(null 表示对象类型或 0 表示数字原语。

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

  3. 然后执行插入

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

  5. Spring Data JDBC 然后尝试将其转换为 UUID 并失败。

在后面的版本中,Spring Data JDBC在第4步意识到它已经有了一个ID,并没有要求驱动生成一个,就跳过了第5步,所以一切都很愉快。

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