如何在Spring Data JPA中重写@CreatedDate实现历史数据时间同步
解决方案:保留Foo历史文档的原始CreatedOn时间
问题核心:当保存FooHistory时,Spring Data MongoDB的Auditing机制会自动更新嵌套Foo对象里的@CreatedDate字段,导致历史记录丢失原始创建时间。以下是几种可行的解决办法:
方案一:自定义Mongo保存事件监听器(推荐)
通过监听FooHistory的保存事件,在Auditing处理后手动重置CreatedOn为原始值。
首先修正Foo类的注解(补全你提到的@CreatedDate),然后创建事件监听器:
import org.joda.time.LocalDate; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.CreatedDate; // 修正后的Foo实体类 public class Foo { @Id private String id; @CreatedDate // 标注自动生成创建时间的注解 private LocalDate createdOn; // 规范小驼峰命名 // getter、setter 自行补充 } public class FooHistory { @Id private String id; private Foo foo; // getter、setter 自行补充 } // 自定义监听器,处理FooHistory保存前的逻辑 import org.springframework.data.mongodb.core.mapping.event.BeforeSaveEvent; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; import org.springframework.data.mongodb.core.MongoOperations; @Component public class FooHistoryAuditListener implements ApplicationListener<BeforeSaveEvent<FooHistory>> { @Override public void onApplicationEvent(BeforeSaveEvent<FooHistory> event) { FooHistory history = event.getSource(); Foo currentFoo = history.getFoo(); if (currentFoo != null && currentFoo.getId() != null) { // 从数据库查询原始Foo的创建时间 MongoOperations mongoOps = event.getMongoOperations(); Foo originalFoo = mongoOps.findById(currentFoo.getId(), Foo.class); if (originalFoo != null) { // 重置为原始创建时间,覆盖Auditing生成的当前时间 currentFoo.setCreatedOn(originalFoo.getCreatedOn()); } } } }
这个监听器会在FooHistory保存前触发,自动从数据库拉取原始Foo的CreatedOn值,确保历史记录保留最初的创建时间。
方案二:拆分DTO与实体类
创建一个不带Auditing注解的FooDTO类,专门用于封装历史记录中的Foo数据,避免Auditing机制干预。
// 数据库持久化用的Foo实体(带Auditing注解) public class Foo { @Id private String id; @CreatedDate private LocalDate createdOn; // 转换为DTO的方法 public FooDTO toDTO() { FooDTO dto = new FooDTO(); dto.setId(this.id); dto.setCreatedOn(this.createdOn); // 其他字段的转换逻辑自行补充 return dto; } // getter、setter 自行补充 } // 历史记录用的FooDTO(无Auditing注解) public class FooDTO { private String id; private LocalDate createdOn; // getter、setter 自行补充 } // 修改FooHistory类,使用DTO替代原始实体 public class FooHistory { @Id private String id; private FooDTO foo; // getter、setter 自行补充 }
保存历史记录时,先把Foo实体转成FooDTO再存入FooHistory,这样Auditing不会处理DTO里的字段,自然保留原始创建时间。
方案三:临时禁用Auditing再保存
在保存FooHistory前后,临时移除并恢复Auditing事件监听器,避免触发CreatedDate的更新。
import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.mapping.event.AuditingEventListener; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Service; @Service public class FooHistoryService { private final MongoTemplate mongoTemplate; private final ApplicationContext applicationContext; private final AuditingEventListener auditingListener; public FooHistoryService(MongoTemplate mongoTemplate, ApplicationContext applicationContext) { this.mongoTemplate = mongoTemplate; this.applicationContext = applicationContext; this.auditingListener = applicationContext.getBean(AuditingEventListener.class); } public void saveHistory(Foo originalFoo) { // 临时移除Auditing监听器 applicationContext.removeApplicationListener(auditingListener); // 创建并保存历史记录 FooHistory history = new FooHistory(); history.setFoo(originalFoo); mongoTemplate.save(history); // 恢复Auditing监听器 applicationContext.addApplicationListener(auditingListener); } }
这种方式适合简单场景,但要注意线程安全,建议在同步方法或单线程环境下使用。
内容的提问来源于stack exchange,提问作者RAKESH YEDLAPPALLI
相关产品推荐
相关产品推荐

