Javers ENTITY_INSTANCE_WITH_NULL_ID 使用 2 个数据库时

Javers ENTITY_INSTANCE_WITH_NULL_ID when using 2 databases

当我将数据存储在 Postgres 中(使用 JPA 延迟加载)并将 javers 存储在 MongoDB

中时,我遇到了这个异常 "ENTITY_INSTANCE_WITH_NULL_ID"

Spring 启动:1.4.0.RELEASE

Sprig 数据 JPA:1.4.0.RELEASE

贾弗斯:2.3.0

我调试发现,如果对象是惰性对象,ID 为空: org.javers.core.metamodel.type.EntityType:88 "Object cdoId = getIdProperty().get(instance);"

当您将一个对象提交给 JaVers 时,它的先前版本将从 JaversRepository 加载并与当前版本(您刚刚传递给 commit() 方法的那个版本)进行比较。 在这种情况下,JaVers 使用 GlobalId 查询找到以前的版本,因此 TypeName + 实体 ID。 这就是实体的 ID 不能为 null 的原因。

有两种可能:

  1. 如果 null ID 在此 class 中是正常的(根据您的域模型),您应该将其映射为 JaVers 中的 ValueObject
  2. 如果您使用的是 Hibernate,则延迟加载代理存在常见问题。对于某些查询,Hibernate 不会 return 您的真实域对象,而是本质上为空的动态代理对象(因此 ID 为空)。这种技术可能看起来很聪明,但在 Hibernate 初始化它们之前会使您的对象成为垃圾。 JaVers 提供 HibernateUnproxyObjectAccessHook 进行清理:域对象的初始化和取消代理。

    JaversBuilder.javers().withObjectAccessHook( new HibernateUnproxyObjectAccessHook()).build()

此挂钩在 javers-spring-boot-starter-sql 中默认启用,但在 javers-spring-boot-starter-mongo 中未启用。如果您正在使用 Mongo starter,请自行创建一个 JaVers bean,并启用挂钩,请参阅 JaversMongoAutoConfiguration

我通过制作自己的访问挂钩解决了这个问题,并使用

添加到 Javers

.withObjectAccessHook(new EntityAccessHook()).build()

public class EntityAccessHook<T> extends HibernateUnproxyObjectAccessHook<T> {

  @Override
  public Optional<ObjectAccessProxy<T>> createAccessor(T entity) {
    Optional<ObjectAccessProxy<T>> accessProxy = super.createAccessor(entity);
    if (accessProxy.isEmpty() && entity instanceof AbstractUuidEntity) {
      return fromEntityInitializer((AbstractUuidEntity) entity);
    }
    return accessProxy;
  }

  private Optional<ObjectAccessProxy<T>> fromEntityInitializer(
      AbstractUuidEntity abstractUuidEntity) {
    return Optional.of(
        new ObjectAccessProxy(
            () -> abstractUuidEntity,
            abstractUuidEntity.getClass(),
            abstractUuidEntity.getId() == null ? UUID.randomUUID() : abstractUuidEntity.getId()));
  }
}