【问题标题】:How to use Mongo Auditing and a UUID as id with Spring Boot 2.2.x?如何在 Spring Boot 2.2.x 中使用 Mongo 审计和 UUID 作为 id?
【发布时间】:2020-04-27 06:39:15
【问题描述】:

我希望使用 UUID id 和 createdAt / updatedAt 字段存储文档。我的解决方案是使用 Spring Boot 2.1.x。在我从 Spring Boot 2.1.11.RELEASE 升级到 2.2.0.RELEASE 之后,我对 MongoAuditing 的测试以createdAt = null 失败。我需要做什么才能再次填充 createdAt 字段?

这不仅仅是一个测试问题。我运行了该应用程序,它的行为与我的测试相同。所有审计字段都保持为空。

我有一个启用 MongoAuditing 和 UUID 生成的配置:

@Configuration
@EnableMongoAuditing
public class MongoConfiguration {
    @Bean
    public GenerateUUIDListener generateUUIDListener() {
        return new GenerateUUIDListener();
    }
}

监听器连接到onBeforeConvert - 我猜这就是麻烦开始的地方。

public class GenerateUUIDListener extends AbstractMongoEventListener<IdentifiableEntity> {
    @Override
    public void onBeforeConvert(BeforeConvertEvent<IdentifiableEntity> event) {
        IdentifiableEntity entity = event.getSource();
        if (entity.isNew()) {
            entity.setId(UUID.randomUUID());
        }
    }
}

文档本身(我删除了 getter 和 setter):

@Document
public class MyDocument extends InsertableEntity {
    private String name;
}


public abstract class InsertableEntity extends IdentifiableEntity {
    @CreatedDate
    @JsonIgnore
    private Instant createdAt;
}

public abstract class IdentifiableEntity implements Persistable<UUID> {
    @Id
    private UUID id;

    @JsonIgnore
    public boolean isNew() {
        return getId() == null;
    }
}

可以在此处找到完整的最小示例(包括测试)https://github.com/mab/auditable 使用 2.1.11.RELEASE 测试成功,使用 2.2.0.RELEASE 失败。

【问题讨论】:

  • BeforeConvertEvent 的执行顺序似乎发生了变化。在 2.2.0 中,首先设置 id,然后 isNew 无法检查是否需要设置 createdAt

标签: java spring mongodb spring-boot spring-data-mongodb


【解决方案1】:

对我来说,最好的解决方案是从事件 UUID 生成切换到基于回调的生成。通过Ordered的实现,我们可以设置新的回调在AuditingEntityCallback之后执行。

public class IdEntityCallback implements BeforeConvertCallback<IdentifiableEntity>, Ordered {
    @Override
    public IdentifiableEntity onBeforeConvert(IdentifiableEntity entity, String collection) {
      if (entity.isNew()) {
        entity.setId(UUID.randomUUID());
      }
      return entity;
    }

    @Override
    public int getOrder() {
      return 101;
    }
}

我使用MongoConfiguration 注册了回调。对于更通用的解决方案,您可能需要查看 AuditingEntityCallback 与 `MongoAuditingBeanDefinitionParser 的注册。

@Configuration
@EnableMongoAuditing
public class MongoConfiguration {
  @Bean
  public IdEntityCallback registerCallback() {
    return new IdEntityCallback();
  }
}

【讨论】:

  • implements ReactiveBeforeConvertCallback 在反应式堆栈上
  • 但是,如果使用响应式堆栈,请跳过@EnableMongoAuditing,因为AuditorAware 目前没有响应式接口。见stackoverflow.com/a/62151824/1098564
【解决方案2】:

MongoTemplatedoInsert() 上以下列方式工作

  • this.maybeEmitEvent - 发出一个事件onBeforeConvertonBeforeSave 等),这样任何AbstractMappingEventListener 都可以像使用GenerateUUIDListener 一样捕捉并采取行动
  • this.maybeCallBeforeConvert - 在转换 回调 之前调用,例如 mongo auditing

就像你在MongoTemplate.classsrc (831-832) 的源代码中看到的那样

protected <T> T doInsert(String collectionName, T objectToSave, MongoWriter<T> writer) {
        BeforeConvertEvent<T> event = new BeforeConvertEvent(objectToSave, collectionName);
        T toConvert = ((BeforeConvertEvent)this.maybeEmitEvent(event)).getSource(); //emit event
        toConvert = this.maybeCallBeforeConvert(toConvert, collectionName); //call some before convert handlers
        ...
}

MongoAudit 仅通过检查entity.isNew() == true 是否将createdAt 标记为新实体

因为您的代码 (UUID) 已经设置了 Id,所以未填充 createdAt(实体不被视为新实体)

您可以执行以下操作(按从好到坏的顺序排列):

  • 忘记UUID 并使用String 作为您的ID,让mongo 自己创建和管理它的实体ID(这实际上是MongoTemplate 的工作方式第811-812 行)
  • 在代码级别保留UUID,在从数据库中插入和检索时从/到String
  • 创建一个自定义存储库,如post
  • 留在2.1.11.RELEASE
  • updateAt设置为GenerateUUIDListener以及id(重命名为NewEntityListener或smth),基本实现审计
  • 实现一个新的 isNew() 逻辑,它不仅仅依赖于实体id

2.1.11.RELEASE 版本中,方法的顺序被翻转 (MongoTemplate.class 804-805),因此您的代码运行良好

作为一种抽象方法,事件的本质是一种发送后忘记(异步兼容),因此更改对象本身是一种非常糟糕的做法,有 NO 被授予者计算顺序,如果有的话

这就是为什么审计基于回调而不是事件,这就是为什么 Pivotal 不(需要)保持版本之间的顺序

【讨论】:

  • 感谢您的详细帖子。我需要保留 UUID。在数据库中有一个字符串的建议是当前的实现。这并不能解决任何问题。对我来说重要的一点是不要依赖事件。我正在研究实现一个自定义存储库。
猜你喜欢
  • 2019-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-24
  • 2018-06-06
  • 2020-05-29
相关资源
最近更新 更多