新建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生命周期注解手动控制字段赋值:
- 移除
lastModifiedDate上的@UpdateTimestamp、lastModifiedBy上的@LastModifiedBy注解 - 在类中添加生命周期回调方法:
@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
相关产品推荐
相关产品推荐

