简体   繁体   English

使用 Spring Data Auditing 在不覆盖 @Created 字段的情况下更新 Couchbase 文档

[英]Update Couchbase documents without overwriting @Created fields with Spring Data Auditing

I have a Couchbase document for which I'd like to enable auditing:我有一个 Couchbase 文档,我想为其启用审计:

@Document(expiry = 0, expiryUnit = TimeUnit.DAYS, touchOnRead = true)
public class Entity {
    @Id
    @GeneratedValue(strategy = GenerationStrategy.USE_ATTRIBUTES, delimiter = ":")
    private String id;

    @IdAttribute(order = 0)
    private String type;

    @IdAttribute(order = 1)
    private String entityGuid;

    @Version
    private long version;
    
    private String firstName;
    
    private String lastName;
    
    private LocalDate dateOfBirth;
    
    @CreatedDate
    private LocalDateTime createTimeStamp;
    
    @LastModifiedDate
    private LocalDateTime lastUpdateTimeStamp;
    
    @CreatedBy
    private String createdBy;
    
    @LastModifiedBy
    private String lastUpdatedBy;

    ...

My configuration:我的配置:

@Data
@Configuration
@EnableCouchbaseAuditing
@EnableConfigurationProperties(CouchbaseProperties.class)
public class EntityCouchConfig extends AbstractCouchbaseConfiguration {

    ...

    @Bean
    public AuditorAware<String> couchAuditing() {
        return () -> Optional.of("my-entity-service");
    }
}

It was my expectation that when performing update operations through the Couchbase template like replaceById() and upsertById() , spring-data would preserve the document's @CreatedDate and @CreatedBy fields, only updating the @LastModifiedDate and @LastModifiedBy .我期望在通过 Couchbase 模板执行更新操作时,如replaceById()upsertById() ,spring-data 将保留文档的@CreatedDate@CreatedBy字段,仅更新@LastModifiedDate@LastModifiedBy

This, however, seems not to be the case.然而,情况似乎并非如此。 When I perform an update, the document's @Created fields are updated as well.当我执行更新时,文档的@Created字段也会更新。 This seems counterintuitive, and would suggest that I first need to pull the document, transfer the @Created fields, and then save that, explicitly making two calls.这似乎违反直觉,并建议我首先需要提取文档,传输@Created字段,然后保存它,明确地进行两次调用。

I have read the spring-data-couchbase documentation on auditing but it's pretty sparse on the expected behavior here.我已经阅读了关于审计的 spring-data-couchbase 文档,但这里的预期行为非常少。

Is retrieving the document to capture the creation info and then updating the only way to do this, or am I implementing auditing wrong?是检索文档以捕获创建信息然后更新执行此操作的唯一方法,还是我实施审计错误?

So my question was based on a flawed premise, because performing Couchbase template operations like upsert() and replaceById() will never perform any auditing logic.所以我的问题是基于一个有缺陷的前提,因为执行像upsert()replaceById()这样的 Couchbase 模板操作永远不会执行任何审计逻辑。

To get auditing to work, it must be used in conjunction with a Couchbase Repository 's save() method.要使审计工作,它必须与 Couchbase Repositorysave()方法结合使用。

The other important bit is that auditing won't do away with two calls to the database if your caller wants to update an entity and doesn't have the current version number and the current auditing data.另一个重要的一点是,如果您的调用者想要更新实体并且没有当前版本号和当前审计数据,则审计不会取消对数据库的两次调用。

In order for spring-data auditing to work on an update operation, you must supply an entity with all audit fields (@CreatedDate/By, @LastModifiedDate/By) and the current version number.为了使 spring-data 审计能够处理更新操作,您必须提供一个具有所有审计字段(@CreatedDate/By、@LastModifiedDate/By)和当前版本号的实体。 That means if your caller doesn't have this information, you must conduct a find call to retrieve it followed by a save once you have added that data to your entity.这意味着如果您的调用者没有此信息,您必须执行查找调用以检索它,然后在将该数据添加到您的实体后进行保存。 Only then will the auditing intelligently preserve the original @Createdxx fields while updating the @Lastxx fields.只有这样,审计才会在更新@Lastxx字段时智能地保留原始@Createdxx字段。

"So my question was based on a flawed premise, because performing Couchbase template operations like upsert() and replaceById() will never perform any auditing logic. “所以我的问题是基于一个有缺陷的前提,因为执行像 upsert() 和 replaceById() 这样的 Couchbase 模板操作永远不会执行任何审计逻辑。

To get auditing to work, it must be used in conjunction with a Couchbase Repository's save() method."要使审计工作,它必须与 Couchbase 存储库的 save() 方法结合使用。”

The repository.save() calls the template.save() which in turn calls one of insertById, replaceById or upsertById (depending on the existence of an @Version field, and if it is populated or not). repository.save() 调用 template.save() ,后者又调用 insertById、replaceById 或 upsertById 之一(取决于 @Version 字段的存在,以及它是否被填充)。

All the couchbase modification operations insert/upsert/replace are called in spring data couchbase by - ReplaceByIdOperationSupport.one(), InsertByIdOperationSupport().one, UpsertByIdOperationSupport.one() convert the entity with ReactiveCouchbaseTemplateSupport.encodeEntity() which does the audit in onBeforeConvert().所有 couchbase 修改操作插入/更新插入/替换在 spring 数据 couchbase 中调用 - ReplaceByIdOperationSupport.one(), InsertByIdOperationSupport().one, UpsertByIdOperationSupport.one() 使用 ReactiveCouchbaseTemplateSupport.encodeEntity() 转换实体,在 onBeforeConvert 中进行审计(). Set logging of org.springframework.data.couchbase.core.mapping.event=TRACE to see.设置 org.springframework.data.couchbase.core.mapping.event=TRACE 的日志记录来查看。

"if your caller wants to update an entity and doesn't have the current version number and the current auditing data." “如果您的调用者想要更新一个实体并且没有当前版本号和当前审计数据。”

All of insert/replace/upsert act on the whole document.所有插入/替换/更新操作都作用于整个文档。 So when you upsert, you overwrite everything, and if you don't already have the current auditing data, you overwrite them.所以当你更新插入时,你会覆盖所有内容,如果你还没有当前的审计数据,你会覆盖它们。 If you don't have the current version number, but your entity has an @Version field, you can leave it null or 0, and repository.save() will call template.save() which will call template.upsertById.如果您没有当前版本号,但您的实体有一个@Version 字段,您可以将其保留为空或 0,并且 repository.save() 将调用 template.save(),后者将调用 template.upsertById。

The markedAudit() method is below. markedAudit() 方法如下。 isNew() is true if the entity has no @Version or has a @Version field is null or zero.如果实体没有@Version 或@Version 字段为空或零,则isNew() 为真。

  public Mono<Object> markAudited(Object object) {
    Assert.notNull(object, "Source object must not be null");
    if (!this.isAuditable(object)) {
      return Mono.just(object);
    } else {
      PersistentEntity<?, ? extends PersistentProperty<?>> entity = this.entities.getRequiredPersistentEntity(object.getClass());
      return entity.isNew(object) ? this.markCreated(object) : this.markModified(object);
    }
  }

"you must conduct a find call to retrieve it followed by a save once you have added that data to your entity. Only then will the auditing intelligently preserve the original @Createdxx fields while updating the @Lastxx fields." “您必须执行查找调用以检索它,然后在将该数据添加到您的实体后进行保存。只有这样,审计才能在更新@Lastxx 字段时智能地保留原始@Createdxx 字段。”

So the final assessment is pretty much correct, although it should work equally well through repository.save(), template.insertById() and template.replaceById() and template.upsertById().所以最终的评估非常正确,尽管它应该通过 repository.save()、template.insertById() 和 template.replaceById() 和 template.upsertById() 同样有效。 (but if you already have fetched the document, your entity it will have the @Version and there's no need to use upsert() instead of replace(). (但如果您已经获取了文档,您的实体将具有 @Version 并且无需使用 upsert() 而不是 replace()。

The only funky behavior is that if you have an entity without an @Version, that has an @Createdxxxx, it will always treat it as new - as it has no @Version to indicated otherwise.唯一奇怪的行为是,如果您有一个没有@Version 的实体,但它有一个@Createdxxxx,它将始终将其视为新实体——因为它没有@Version 以其他方式指示。

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

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