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

Spring Boot更新方法调用删增报唯一键重复错误求助

解决Spring事务内删除后新增触发唯一键冲突问题

问题根源

你遇到的问题核心是Spring动态代理的机制限制:在同一个类内部调用带有@Transactional注解的方法时,Spring不会创建代理对象来拦截方法调用,所以deleteById和addSemester上的Propagation.REQUIRES_NEW属性完全没生效。整个updateSemesterById的操作处于同一个事务中,删除操作的修改还没提交到数据库,新增操作就执行了,此时数据库里旧的学期记录依然存在,触发唯一键约束冲突。

解决方案

方案1:拆分事务方法到独立Service类

把需要独立事务的方法抽离到另一个Service类中,通过依赖注入调用,这样Spring会为这个独立Service生成代理,确保REQUIRES_NEW事务生效。

步骤1:创建独立的事务处理Service

@Service
public class SemesterTransactionHandler {

    private final SemesterRepository semesterRepository;

    // 构造注入(推荐)
    public SemesterTransactionHandler(SemesterRepository semesterRepository) {
        this.semesterRepository = semesterRepository;
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addSemester(Long semesterId, LocalDate startDate, LocalDate endDate) {
        // 注意:原代码中semesterList是空集合,需构造实际的Semester对象
        Set<Semester> semesterList = new HashSet<>();
        semesterList.add(new Semester(semesterId, startDate, endDate)); // 根据实际实体类调整构造参数
        semesterRepository.saveAll(semesterList);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void deleteById(Long semesterId) {
        semesterRepository.deleteBySemesterId(semesterId);
    }
}

步骤2:在原Service中注入并调用

@Service
public class SemesterService {

    private final SemesterTransactionHandler transactionHandler;

    public SemesterService(SemesterTransactionHandler transactionHandler) {
        this.transactionHandler = transactionHandler;
    }

    public void updateSemesterById(Long semesterId, LocalDate startDate, LocalDate endDate) {
        transactionHandler.deleteById(semesterId);
        transactionHandler.addSemester(semesterId, startDate, endDate);
    }
}

方案2:使用AopContext获取代理对象

通过AopContext获取当前类的代理对象,调用自身的事务方法,强制触发代理拦截。

步骤1:开启代理暴露

在Spring配置类中添加exposeProxy = true:

@EnableAspectJAutoProxy(exposeProxy = true)
@Configuration
public class AppConfig {
    // 其他配置内容
}

步骤2:修改updateSemesterById方法

@Service
public class SemesterService {

    private final SemesterRepository semesterRepository;

    public SemesterService(SemesterRepository semesterRepository) {
        this.semesterRepository = semesterRepository;
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addSemester(Long semesterId, LocalDate startDate, LocalDate endDate) {
        Set<Semester> semesterList = new HashSet<>();
        semesterList.add(new Semester(semesterId, startDate, endDate));
        semesterRepository.saveAll(semesterList);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void deleteById(Long semesterId) {
        semesterRepository.deleteBySemesterId(semesterId);
    }

    public void updateSemesterById(Long semesterId, LocalDate startDate, LocalDate endDate) {
        // 通过代理对象调用事务方法
        SemesterService proxy = (SemesterService) AopContext.currentProxy();
        proxy.deleteById(semesterId);
        proxy.addSemester(semesterId, startDate, endDate);
    }
}

额外注意点

原代码中addSemester方法的semesterList是空集合,这会导致新增操作没有实际数据写入数据库,需要根据业务逻辑构造正确的Semester对象添加到集合中。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 20:37:52