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

新建JPA实体时如何保持@LastModifiedBy为空 仅更新时赋值

Spring JPA新建实体时不自动填充lastModified字段的实现方案

Spring Data JPA默认的AuditingEntityListener会在实体新建、更新两个生命周期节点都触发审计字段填充,导致新建实体时lastModifiedBy、lastModifiedDate会被自动赋上和创建字段相同的值,和需求不符。可以通过以下两种方案实现目标效果:


方案1:自定义审计监听器(推荐,无侵入兼容原生审计能力)

这个方案完全兼容Spring JPA原生审计逻辑,只修改新建阶段的字段填充规则,不需要重复实现审计赋值逻辑。

  • 调整实体类配置
    移除lastModifiedDate字段上的@UpdateTimestamp注解(该注解由Hibernate提供,无法区分新建/更新场景,会在所有持久化操作时自动赋值),同时将默认的审计监听器替换为自定义实现:
    // 替换默认的AuditingEntityListener
    @EntityListeners(CustomAuditingEntityListener.class)
    // 其余类注解保持不变
    public class YourAuditEntity {
        @CreationTimestamp
        @Column(name = "created_date", nullable = false)
        @JsonIgnore
        private LocalDateTime createdDate;
    
        @CreatedBy
        @Column(name = "created_by", nullable = false)
        @JsonIgnore
        private Long createdBy;
    
        // 移除@UpdateTimestamp注解
        @Column(name = "modified_date")
        @JsonIgnore
        private LocalDateTime lastModifiedDate;
    
        @LastModifiedBy
        @Column(name = "modified_by")
        @JsonIgnore
        private Long lastModifiedBy;
    
        // 其余字段、方法省略
    }
    
  • 实现自定义审计监听器
    继承原生AuditingEntityListener,重写新建、更新的回调逻辑:新建时仅标记创建相关字段,不处理修改字段;更新时才执行修改字段的填充:
    import org.springframework.data.auditing.AuditingHandler;
    import org.springframework.data.jpa.domain.support.AuditingEntityListener;
    import jakarta.persistence.PrePersist;
    import jakarta.persistence.PreUpdate;
    
    public class CustomAuditingEntityListener extends AuditingEntityListener {
    
        @Override
        @PrePersist
        public void touchForCreate(Object target) {
            AuditingHandler handler = getAuditingHandler();
            if (handler != null) {
                // 仅处理@CreatedDate、@CreatedBy字段
                handler.markCreated(target);
            }
        }
    
        @Override
        @PreUpdate
        public void touchForUpdate(Object target) {
            AuditingHandler handler = getAuditingHandler();
            if (handler != null) {
                // 更新时才处理@LastModifiedDate、@LastModifiedBy字段
                handler.markModified(target);
            }
        }
    }
    
  • 注册监听器到Spring容器
    在JPA配置类中注入自定义监听器,避免AuditingHandler为空的问题:
    @Configuration
    @EnableJpaAuditing(auditorAwareRef = "auditorProvider")
    public class JpaAuditConfig {
    
        @Bean
        public AuditorAware<Long> auditorProvider() {
            return () -> {
                AppUserPrincipal principal = AuthUtils.getCurrentUser();
                return Optional.of(principal.getUserId());
            };
        }
    
        @Bean
        public CustomAuditingEntityListener customAuditingEntityListener(AuditingHandler auditingHandler) {
            CustomAuditingEntityListener listener = new CustomAuditingEntityListener();
            listener.setAuditingHandler(auditingHandler);
            return listener;
        }
    }
    

方案2:JPA生命周期回调手动控制(适合小型项目快速实现)

如果不想自定义监听器,可以直接在实体类(或公共审计父类)中通过JPA生命周期注解手动控制字段赋值:

  1. 移除lastModifiedDate上的@UpdateTimestamp、lastModifiedBy上的@LastModifiedBy注解
  2. 在类中添加生命周期回调方法:
    @EntityListeners(AuditingEntityListener.class)
    @Entity
    public class BaseAuditEntity {
        // createdDate、createdBy字段注解保持不变
        @CreationTimestamp
        @Column(name = "created_date", nullable = false)
        @JsonIgnore
        private LocalDateTime createdDate;
    
        @CreatedBy
        @Column(name = "created_by", nullable = false)
        @JsonIgnore
        private Long createdBy;
    
        @Column(name = "modified_date")
        @JsonIgnore
        private LocalDateTime lastModifiedDate;
    
        @Column(name = "modified_by")
        @JsonIgnore
        private Long lastModifiedBy;
    
        // 新建完成默认审计赋值后,手动清空修改字段
        @PrePersist
        void clearModifiedFieldOnCreate() {
            this.lastModifiedDate = null;
            this.lastModifiedBy = null;
        }
    
        // 更新时手动给修改字段赋值
        @PreUpdate
        void fillModifiedFieldOnUpdate() {
            this.lastModifiedDate = LocalDateTime.now();
            AppUserPrincipal principal = AuthUtils.getCurrentUser();
            if (principal != null) {
                this.lastModifiedBy = principal.getUserId();
            }
        }
    }
    

注意:该方案需要手动维护修改字段的赋值逻辑,如果鉴权上下文获取逻辑有调整,需要同步修改此处代码,适合审计逻辑简单的小型项目使用。


效果验证

配置完成后执行操作可以得到预期结果:

  • 新建实体入库时:created_date、created_by自动填充当前操作时间和用户ID,modified_date、modified_by保持null
  • 后续更新实体时:modified_date、modified_by自动填充更新操作的时间和用户ID,创建字段值不发生变化

内容的提问来源于stack exchange,提问作者M. Merim

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 10:45:39