save/update后调用findById返回createdBy等审计字段为空问题咨询
问题诊断与解决方案
代码存在的核心问题
- 更新逻辑错误导致审计字段丢失
你通过getOne获取到已有的持久化实体后,没有直接修改该实体的属性,而是新创建了一个Account对象,仅给新对象赋值了id和accountname,未携带原有实体的@CreatedBy、@CreatedDate等审计字段值。调用save方法时,JPA会将这个未赋值审计字段的新对象作为全量更新数据同步到持久化上下文,未赋值的字段就会被置为null,后续查询自然返回空值。 - 时间格式不一致属于时区转换问题
你看到的返回时间带T、和数据库存储时间不一致是JSON序列化时区转换导致的:数据库存储的是你服务所在时区的时间,序列化默认返回UTC时区的ISO标准格式,两者实际是同一个时间点,只是展示格式与时区不同。 - 未刷写持久化上下文导致读取缓存数据
默认save操作仅将改动同步到JPA持久化上下文(一级缓存),不会立即刷入数据库,此时调用findById默认优先读取一级缓存中的数据,不会去数据库拉取最新持久化的结果。
修复方案
1 修正更新逻辑,避免全量覆盖
不要新创建实体对象,直接修改getOne查询到的原有持久化实体的属性,原有不可更新的审计字段会被自动保留,审计框架仅会更新@LastModifiedBy、@LastModifiedDate等需要更新的字段。
2 安全刷写持久化上下文,不破坏事务
你不需要使用saveAndFlush,可以在查询前单独调用flush()方法,仅将当前上下文的改动刷入数据库,不会提前提交事务,不会破坏原有事务模型。如果需要强制读取数据库最新数据,可以手动清空一级缓存中对应实体。
修复后的代码示例:
@GetMapping(value = "/saveandshow") public Object GetsavedValue(@RequestParam("name") String name,@RequestParam("id") Long id ) { Account acc_find = accRepo.getOne(id); // 直接修改原有持久化对象属性,无需新建对象 acc_find.setAccountname(name); Account acc = accRepo.save(acc_find); // 仅刷写改动到数据库,不提交事务 accRepo.flush(); // 可选:清空当前实体的一级缓存,强制查询走数据库 entityManager.clear(); Optional<Account> fetchAccount = accRepo.findById(acc.getId()); System.out.println("Accont "+fetchAccount); return "Success"; }
3 统一时间序列化格式
两种方案可解决时间格式与时区不一致问题:
- 全局配置:在Spring Boot配置文件中添加以下配置
spring.jackson.time-zone=GMT+8 spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
- 字段单独配置:在审计日期字段上添加
@JsonFormat注解
@CreatedDate @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date createdDate; @LastModifiedDate @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date lastModifiedDate;
4 确认审计字段配置
检查实体类的审计字段是否设置了updatable = false,避免更新操作覆盖创建类审计字段:
@CreatedBy @Column(updatable = false) private String createdBy; @CreatedDate @Column(updatable = false) private Date createdDate;
同时确认实体类上已添加@EntityListeners(AuditingEntityListener.class)注解,审计功能正常生效。
内容的提问来源于stack exchange,提问作者vivek java
相关产品推荐
相关产品推荐

