Spring Boot JPA审计更新实体时@CreatedBy与@CreatedDate为空问题
解决Spring Boot JPA审计更新时创建字段未填充的问题
问题根源
更新操作时,传入saveAndFlush的Application对象中,createdBy和creationDatetime处于未初始化状态(这两个字段标记了updatable=false,更新逻辑不会修改它们,而你传入的更新对象通常不会携带这些值)。JPA的save方法处理更新时,默认基于传入的实体对象进行状态合并,不会自动从数据库加载这些不可更新的字段,导致返回的实体中这两个字段仍未初始化,最终序列化JSON时抛出lateinit property createdBy has not been initialized错误。
解决方案
方式1:更新后重新查询实体(推荐)
调用saveAndFlush完成更新后,通过Repository的findById重新查询该实体,获取包含所有字段的完整数据。
Kotlin 实现示例
@Service class ApplicationService(private val repo: ApplicationRepository) { fun updateApplication(id: UUID, updatedApp: Application): Application { repo.saveAndFlush(updatedApp) // 重新查询获取完整实体 return repo.findById(id).orElseThrow() } }
Java 实现示例
@Service public class ApplicationService { private final ApplicationRepository repo; public ApplicationService(ApplicationRepository repo) { this.repo = repo; } public Application updateApplication(UUID id, Application updatedApp) { repo.saveAndFlush(updatedApp); return repo.findById(id).orElseThrow(() -> new RuntimeException("Application not found")); } }
方式2:调整实体字段定义(临时兼容方案)
将Auditable中的lateinit字段改为可空类型,避免序列化时抛出未初始化错误。但这种方式会弱化数据完整性约束,不建议长期使用。
Kotlin 修改示例
@MappedSuperclass @EntityListeners(AuditingEntityListener::class) abstract class Auditable { @CreatedBy @Column(nullable = false, updatable = false) var createdBy: String? = null @CreatedDate @Column(nullable = false, updatable = false) var creationDatetime: Instant? = null @LastModifiedBy @Column lateinit var lastModifiedBy: String @LastModifiedDate @Column lateinit var lastModifiedDate: Instant }
Java 修改示例
@MappedSuperclass @EntityListeners(AuditingEntityListener.class) public abstract class Auditable { @CreatedBy @Column(nullable = false, updatable = false) private String createdBy; @CreatedDate @Column(nullable = false, updatable = false) private Instant creationDatetime; @LastModifiedBy @Column private String lastModifiedBy; @LastModifiedDate @Column private Instant lastModifiedDate; // 生成Getter、Setter或使用Lombok的@Data注解 }
方式3:配合@DynamicUpdate优化性能
在实体类上添加@DynamicUpdate注解,让JPA仅更新实际修改过的字段,减少数据库更新开销(需配合方式1使用,无法单独解决字段缺失问题)。
Kotlin 示例
@Entity @Table(name = "application", schema = "public") @DynamicUpdate data class Application( @Id @Column val id: UUID, @Column val name: String ): Auditable()
Java 示例
@Entity @Table(name = "application", schema = "public") @DynamicUpdate public class Application extends Auditable { @Id @Column private UUID id; @Column private String name; // 构造器、Getter、Setter }
关键说明
- 标记
updatable=false的字段,JPA不会在更新时修改它们,但也不会主动从数据库加载这些字段到传入的实体对象中。 - 重新查询是最可靠的解决方案,能确保获取到完整的实体数据,彻底避免序列化错误。
内容的提问来源于stack exchange,提问作者Leonardo
相关产品推荐
相关产品推荐

