繁体   English   中英

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

[英]How to use Mongo Auditing and a UUID as id with Spring Boot 2.2.x?

我希望使用 UUID id 和 createdAt / updatedAt 字段存储文档。 我的解决方案是使用 Spring Boot 2.1.x。 从 Spring Boot 2.1.11.RELEASE 升级到 2.2.0.RELEASE 后,我对 MongoAuditing 的测试失败了, createdAt 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注册了回调。 对于更通用的解决方案,您可能需要查看AuditingEntityCallback与`MongoAuditingBeanDefinitionParser 的注册。

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

MongoTemplatedoInsert()上的工作方式如下

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

就像你在MongoTemplate.class src (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标记createdAt仅通过检查以新的实体,如果entity.isNew() == true

因为您的代码 (UUID) 已经设置了未填充createdAtId (该实体不被视为新的)

您可以执行以下操作(按最佳到最差排序):

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

2.1.11.RELEASE版本中,方法的顺序被翻转( MongoTemplate.class 804-805)所以你的代码工作正常

作为一个抽象的方法,事件的性质是要排序的发送和忘记(异步兼容),所以这是一个非常不好的做法,改变物体本身,没有专营公司计算的顺序,如果有的话

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM