Spring Data MongoDB - 使用自定义 Id 字段时注释 @CreatedDate 不起作用
Posted
技术标签:
【中文标题】Spring Data MongoDB - 使用自定义 Id 字段时注释 @CreatedDate 不起作用【英文标题】:Spring Data MongoDB - Annotation @CreatedDate does not work while using with custom Id field 【发布时间】:2019-06-13 09:24:31 【问题描述】:我有一个简单的 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 实现来区分插入和更新请求。
【参考方案1】:
我自己也遇到了这个问题,这是因为您自己创建了 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 将起作用。【参考方案2】:如果那是您真正的课程 (Profile
),那么您无法使用标准工具进行任何操作。
您的isNew
方法将始终返回false
,因为您自己设置了用户名,当Profile
即将被Spring 保存时,它将检查isNew
,您可能已经设置了username
. @LastModifiedDate
适用于您的情况,但 @CreatedDate
不会。
如果您没有可以在 isNew
方法中使用的其他字段,那么您必须手动设置 createdDate
的值(或者可能有某种拦截器可以包装所有 mongo 模板方法,但我不会那样做)。
例如,检查具有给定用户名的配置文件是否已经存在于数据库中,如果存在,只需获取它的 createdDate(您可以在此处使用投影)并设置为您将要保存的配置文件。否则将 createdDate 设置为新日期。
【讨论】:
【参考方案3】:只需将@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
【讨论】:
【参考方案4】:对我来说,我只是这样做:
@CreatedDate
@Field("created_date")
@JsonIgnore
private Instant createdDate = Instant.now();
并确保 Get/Set 可用。 希望对您有所帮助
【讨论】:
【参考方案5】:我有类似的情况,有时我需要手动设置 id,如果不先搜索数据库来确定,真的无法提前知道 id 是新的还是更新的。我也知道在实体的生命周期中,我将多次更新我的实体,但只创建一次。所以为了避免额外的数据库操作,我选择了这个解决方案:
Profile savedProfile = profileRepo.save(profile);
if (savedProfile.getCreatedDate() == null )
savedProfile.setCreatedDate(savedProfile.getLastModifiedDate());
savedProfile = profileRepo.save(profile);
这是利用我在 Profile 上还有一个 @LastModifiedDate
字段这一事实,该字段在保存实体时始终由 spring 数据更新。
【讨论】:
【参考方案6】:如 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;
【讨论】:
以上是关于Spring Data MongoDB - 使用自定义 Id 字段时注释 @CreatedDate 不起作用的主要内容,如果未能解决你的问题,请参考以下文章
如何在带有自定义过滤器的 Spring Data mongodb 中使用分页和排序?
Spring Boot MongoDB REST - 自定义存储库方法