You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 07:10:19