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

Spring Data JDBC中如何防止@CreatedBy与@CreatedDate字段在更新时被修改

解决Spring Data JDBC更新时createdBy/createdDate被置为null的问题

嘿,这个问题我之前也碰到过,Spring Data JDBC确实没提供JPA那种updatable=false的注解支持,不过有几个靠谱的解决方案可以帮你搞定更新时覆盖created字段的问题:

方案1:使用实体回调(Entity Callback)控制更新逻辑

这是Spring Data JDBC官方推荐的扩展方式,通过实现BeforeUpdateCallback,在更新操作执行前保留原实体的创建信息。

创建一个回调类并注册为Spring Bean:

@Component
public class AuditEntityUpdateCallback implements BeforeUpdateCallback<AuditEntity> {

    @Override
    public AuditEntity onBeforeUpdate(AuditEntity entity, AggregateChange aggregateChange) {
        // 从AggregateChange中获取数据库里的原始实体状态
        Optional<Object> originalEntityOpt = aggregateChange.getEntity().getOriginalValue();
        if (originalEntityOpt.isPresent()) {
            AuditEntity originalAudit = (AuditEntity) originalEntityOpt.get();
            // 将原始的createdBy和createdOn赋值回当前实体,避免被覆盖为null
            entity.setCreatedBy(originalAudit.getCreatedBy());
            entity.setCreatedOn(originalAudit.getCreatedOn());
        }
        return entity;
    }
}

原理:当执行更新时,Spring Data JDBC会加载数据库中已有的实体数据,我们通过回调把原始的创建信息重新赋值给待更新的实体,这样最终生成的SQL更新语句就会带上原有值,而不是null。

方案2:在实体setter中添加保护逻辑

通过手动实现createdBy和createdOn的setter方法,只允许初始赋值,后续修改(包括设置为null)会被忽略。

修改你的AuditEntity类:

@Data // Lombok
public class AuditEntity {
    @JsonIgnore
    @LastModifiedDate
    private String updatedOn;
    @JsonIgnore
    @LastModifiedBy
    private String updatedBy;
    @JsonIgnore
    @CreatedDate
    private String createdBy;
    @JsonIgnore
    @CreatedBy
    private String createdOn;

    // 手动实现createdBy的setter,仅允许初始设置
    public void setCreatedBy(String createdBy) {
        if (this.createdBy == null) {
            this.createdBy = createdBy;
        }
        // 已有值时不做任何操作,避免被覆盖
    }

    // 同理实现createdOn的setter
    public void setCreatedOn(String createdOn) {
        if (this.createdOn == null) {
            this.createdOn = createdOn;
        }
    }
}

原理:当更新操作试图修改createdBy或createdOn时,setter会忽略新的赋值(包括null),从而保留最初插入时的数值。

方案3:自定义Repository实现合并更新

创建一个通用的基础Repository,在更新时先查询原实体,仅复制需要修改的字段,跳过创建信息字段。

定义基础Repository接口:

public interface BaseRepository<T, ID> extends CrudRepository<T, ID> {

    default T update(ID id, T updatedEntity) {
        // 查询数据库中的原始实体
        T originalEntity = findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Entity with id " + id + " not found"));
        
        // 使用BeanUtils复制属性,排除createdBy和createdOn字段
        BeanUtils.copyProperties(updatedEntity, originalEntity, "createdBy", "createdOn");
        
        // 保存合并后的实体
        return save(originalEntity);
    }
}

然后让你的业务Repository继承这个BaseRepository,之后更新时调用update()方法而非默认的save()方法即可。

方案推荐

优先选择方案1,因为它完全贴合Spring Data JDBC的设计理念,不需要修改业务代码的调用方式,也无需手动处理实体合并逻辑,是最优雅的解决方案。

内容的提问来源于stack exchange,提问作者Ravi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 21:22:41