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

Spring-data-JDBC使用应用生成ID时执行save更新报错如何解决

问题根因

Spring Data JDBC 对实体执行插入/更新的逻辑判断,早于BeforeSave回调的触发时机:

  1. 首次调用save方法时:实体ID初始为null,框架判定为新实体,走插入逻辑,之后才触发BeforeSave回调生成并赋值ID,最终插入数据库成功。
  2. 二次调用save执行更新时:实体已提前携带ID,框架判定为存量实体走更新逻辑,默认的状态判断规则不匹配应用侧生成ID的场景,就会抛出ID不存在的更新语义异常。

解决方案

方案1:实体实现Persistable接口自定义状态判断(最推荐)

自行控制实体是否为新实例的判断逻辑,适配应用生成ID的场景:

import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PostLoad;
import org.springframework.data.annotation.Transient;
import org.springframework.data.domain.Persistable;

public class DomainObjectLongId implements Persistable<Long> {

    @Id
    private Long id;
    // 其余业务字段省略

    // transient 标记字段不参与持久化
    @Transient
    private boolean isNew;

    @Override
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @Override
    public boolean isNew() {
        return isNew;
    }

    // 新建实体、生成ID时调用该方法标记为新实例
    public void markAsNew() {
        this.isNew = true;
    }

    // 从数据库查询实体后自动标记为存量实例
    @PostLoad
    private void markAsNotNew() {
        this.isNew = false;
    }
}

你原有BeforeSave回调中生成ID的逻辑无需修改,只要在生成ID后调用markAsNew()方法即可。

方案2:通过@Version乐观锁字段自动判断

如果你的业务本身需要乐观锁能力,直接添加@Version注解字段即可自动解决状态判断问题:

import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Version;
import java.util.UUID;

public class DomainObjectUuid {

    @Id
    private UUID id;
    // 新增乐观锁字段,新建实体时该字段为null,框架自动判定为新实例
    @Version
    private Integer version;
    // 其余业务字段省略
}

插入操作完成后框架会自动给version字段赋值为1,后续更新操作自动走更新逻辑,同时自带乐观锁能力。

方案3:全局自定义IsNewStrategy

如果项目全量实体都采用应用侧生成ID的模式,可以全局配置判断规则,无需每个实体单独适配:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.mapping.model.PersistentEntityIsNewStrategy;

@Configuration
public class JdbcCustomConfig {

    @Bean
    public JdbcMappingContext jdbcMappingContext() {
        JdbcMappingContext mappingContext = new JdbcMappingContext();
        mappingContext.setIsNewStrategyFactory(entity -> 
            PersistentEntityIsNewStrategy.of(entity, idProperty -> {
                // 替换为你项目通用的新实例判断逻辑即可
                Object id = entity.getPropertyAccessor(entity).getProperty(idProperty);
                return id == null;
            })
        );
        return mappingContext;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 14:45:05