【问题标题】:Spring Data MongoDB - Annotation @CreatedDate does not work while using with custom Id fieldSpring Data MongoDB - 使用自定义 Id 字段时注释 @CreatedDate 不起作用
【发布时间】:2018-04-13 18:17:55
【问题描述】:

我有一个简单的 Persistable 类:

public class Profile implements Persistable<String>{

    @Id
    private String username;

    @CreatedDate
    public Date createdDate;

    public Profile(String username) {
        this.username = username;
    }

    @Override
    public String getId() {
        return username;
    }

    @Override
    public boolean isNew() {
        return username == null;
    }
}

还有一个简单的存储库:

public interface ProfileRepository extends MongoRepository<Profile, String> {

}

我的 Spring Boot 应用程序类也使用 @EnableMongoAuditing 进行了注释。但我仍然无法使用 @CreatedDate 注释。

ProfileRepository.save(new Profile("user1")) 写入没有字段 createdDate 的实体。我做错了什么?

编辑:这是我的应用程序类(没有@EnableMongoRepositories,但它可以工作,因为我猜存储库位于子包中)

@SpringBootApplication
@EnableMongoAuditing
public class Application {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }
}

编辑:同样添加注释 EnableMongoRepositories 并没有改变任何东西。

【问题讨论】:

  • 它对我有用...
  • 奇怪.. 2 小时我试图找出为什么它对我不起作用。我已经创建了我的实体,其 ID 为“new Profile(“username”)”,然后保存了它。你也可以这样试试吗
  • 你能显示配置吗?我猜你有@EnableMongoRepositories。
  • 在您的情况下,因为您始终设置用户名,所以 spring 存储库始终尝试将实体处理为更新(保存)而不是插入,因为 isNew 始终返回 false。所以 createdDate 字段永远不会被设置。我敢打赌,如果您包含 lastModified 字段,它将被设置。尝试实现Auditable 接口并在自定义 ID 的情况下自己设置审计字段。更多here
  • 进一步思考可能会滚动我们自己的Auditable 实现可能无法正常工作,因为 isNew 将返回 false 并且 spring 存储库将仅从实体复制 lastModifed 字段。所以看起来你必须更新 isNew 实现来区分插入和更新请求。

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


【解决方案1】:

只需将@Version 字段添加到@Document 类并离开@EnableMongoAuditing

@Document
public class Profile implements Persistable<String>{

     @Version      
     private Long version;
    
     @Id
     private String username;

     @CreatedDate
     public Date createdDate;

     public Profile(String username) {
         this.username = username;
     }

     @Override
     public String getId() {
         return username;
     }

     @Override
     public boolean isNew() {
         return username == null;
     }
 }

这是一个相关问题:https://jira.spring.io/browse/DATAMONGO-946

【讨论】:

    【解决方案2】:

    如 Spring Data MongoDB 问题DATAMONGO-946 中所述,创建日期功能使用isNew() 方法来确定是否应设置创建日期,因为实体是新实体。在您的情况下,您的 isNew 方法始终返回 false,因为始终设置了 username

    问题中的 cmets 为这个问题提供了两种可能的解决方案。

    Persistable解决方案

    第一个选项是修复isNew 策略,使其正确注册新对象。 cmets 中建议的一种方法是更改​​实现以检查 createdDate 字段本身,因为它应该只设置在非新对象上。

    @Override
    public boolean isNew() {
        return createdDate == null;
    }
    

    持久化实体解决方案

    第二个选项是从实现Persistable 改为使用持久化实体,并使用@Version 注释在持久化的MongoDB 实体中注入version 属性。请注意,这更改数据的持久化方式,因为它会在数据中添加一个自动递增的version 字段。

    import org.springframework.data.mongodb.core.mapping.Document;
    
    @Document
    public class Profile {
        @Id
        private String username;
    
        @CreatedDate
        public Date createdDate;
    
        @Version
        public Integer version;
    
        public Profile(String username) {
            this.username = username;
        }
    }
    

    【讨论】:

      【解决方案3】:

      我自己也遇到了这个问题,这是因为您自己创建了 id。

      public Profile(String username) {
              this.username = username;
          }
      

      通过这样做,mongo 认为它不是一个新对象,并且不使用 @CreatedDate 注释。您也可以使用 @Document 注释而不是实现 Persistable 类,如下所示:

      @Document
      public class Profile{}
      

      【讨论】:

      • 顺便说一句,您永远不应该将用户名设置为 id,如果您决定用户名可以更改怎么办。 id 永远不应该改变,我建议你做 2 个变量,Id 和 Username。
      • 你不知道我可以想象现实生活中用户名可以成为有效ID的例子
      • 在我看来这仍然是一个不好的做法。
      • 好吧,恕我直言,总的来说,我们应该更喜欢自然主键。当然,我们不能破坏不变量,例如一个名字可以改变。尽可能选择自然主键而不是代理项
      • 但无论如何,我认为上述问题正在发生,因为他使用的是自然 id,他正在定义自己的 id。如果他将使用生成的 id(由 mongo 生成),则 creationDate 将起作用。
      【解决方案4】:

      我有类似的情况,有时我需要手动设置 id,如果不先搜索数据库确定,真的无法提前知道 id 是新的还是更新的。我也知道在实体的生命周期中,我将多次更新我的实体,但只创建一次。所以为了避免额外的数据库操作,我选择了这个解决方案:

          Profile savedProfile = profileRepo.save(profile);
          if (savedProfile.getCreatedDate() == null ){
              savedProfile.setCreatedDate(savedProfile.getLastModifiedDate());
              savedProfile = profileRepo.save(profile);
          }
      

      这是利用我在 Profile 上还有一个 @LastModifiedDate 字段这一事实,该字段在保存实体时始终由 spring 数据更新。

      【讨论】:

        【解决方案5】:

        对我来说,我只是这样做:

            @CreatedDate
            @Field("created_date")
            @JsonIgnore
            private Instant createdDate = Instant.now();
        

        并确保 Get/Set 可用。 希望对您有所帮助

        【讨论】:

          【解决方案6】:

          如果那是您真正的课程 (Profile),那么您无法使用标准工具进行任何操作。

          您的isNew 方法将始终返回false,因为您自己设置了用户名,当Profile 即将被Spring 保存时,它将检查isNew,您可能已经设置了username . @LastModifiedDate 适用于您的情况,但 @CreatedDate 不会。

          如果您没有可以在 isNew 方法中使用的其他字段,那么您必须手动设置 createdDate 的值(或者可能有某种拦截器可以包装所有 mongo 模板方法,但我不会那样做)。

          例如,检查具有给定用户名的配置文件是否已经存在于数据库中,如果存在,只需获取它的 createdDate(您可以在此处使用投影)并设置为您将要保存的配置文件。否则将 createdDate 设置为新日期。

          【讨论】:

            猜你喜欢
            • 2013-12-27
            • 2016-06-05
            • 2017-08-10
            • 2018-11-29
            • 2013-01-15
            • 2020-07-27
            • 1970-01-01
            相关资源
            最近更新 更多