如何在Spring Boot 2.2.x中使用Mongo审计和UUID作为id?



我想使用UUID ID存储文档并创建At/updateAt 字段。我的解决方案是使用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 时,它失败了。

对我来说,最好的解决方案是从事件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注册了回调。对于更通用的解决方案,您可能需要查看使用'MongoAuditingBeanDefinitionParser注册AuditingEntityCallback

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

doInsert()上以以下方式工作

  • 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( 已设置了未填充createdAtId(实体不被视为新实体(

您可以执行以下操作(从最好到最差排序(:

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

在版本2.1.11.RELEASE中,方法的顺序被翻转(MongoTemplate.class804-805(,因此您的代码工作正常

作为一种抽象方法,事件的本质是发送和忘记(异步兼容(,因此更改对象本身是一种非常糟糕的做法,计算顺序没有受让人,如果有的话

这就是为什么审计建立在回调而不是事件上的原因,这就是为什么 Pivotal 不(需要(保持版本之间的顺序

最新更新