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

Spring Boot中@Transactional作用及删除操作失效原因解析

问题描述

我正在使用Java Spring Boot开发,现有如下服务层函数:

@Override
public QuestionDTO updateQuestion(Long id, QuestionDTO updateData) {
    Question question = questionRepository.findById(id).orElseThrow(() -> new RuntimeException());
    if(updateData.getText().length() > 0) {
        question.setText(updateData.getText());
    }

    List<Answer> newAnswers = new ArrayList<>();


    // Delete old answers
    // some filtering logic that would set isPresent variable

        // for i->0 -> arr.size
        if(!isPresent) {
            Long x = existingAnswer.getId(); // the answer with this id needs to be deleted
            answerRepository.deleteById(x);
        }
    
        // for i->0 -> arr.size
        {   
           // some logic to update existing answer entity
            existingAnswer.setIsCorrect(a.getIsCorrect());
            answerRepository.save(existingAnswer);
            justUpdate = true;
            break;
        }
        

        if(!justUpdate) {
            a.setQuestion(question);
            newAnswers.add(a);
        }
    

    LoggingController.getLogger().info("DONE UPDATE! Final array is:");
    question.setAnswers(newAnswers);

    // Save new array of answers for question
    Question updatedQuestion = questionRepository.save(question);
    return QuestionMapper.mapToQuestionDTO(updatedQuestion);
}

Question和Answer均为实体类,我的PostgreSQL数据库结构如下:

Question表

idtext
1"What is the capital of France?"
2"How many countries..."

Answer表

idtextquestion_id (fk)
1"Answer A"1
2"Answer B"1

当我在该函数上添加@Transactional注解时,所有数据库操作均可正常执行;未添加该注解时,删除操作失效,但其他操作正常。请问删除操作失效的原因是什么?@Transactional注解的具体作用又是什么?


问题解答

一、删除操作失效的原因

  1. 持久化上下文缓存不一致
    没加@Transactional时,answerRepository.deleteById(x)会单独开启一个独立事务执行删除,执行完立即提交到数据库。但此时你之前查询出来的question对象还在当前线程的Hibernate Session(持久化上下文)缓存中,它关联的旧Answer集合并未更新。后续执行question.setAnswers(newAnswers)并调用questionRepository.save(question)时,Hibernate会根据Session缓存里的question状态生成SQL——它仍认为已删除的Answer和question保持关联,甚至会把已删除的Answer重新关联回去,最终表现为删除操作失效。

  2. 独立事务的冲突问题
    无外层事务包裹时,删除Answer和更新Question是两个完全独立的事务。删除操作本身可能成功,但后续更新Question时,Session缓存的旧关联关系会触发Hibernate的自动同步,将数据库中已删除的Answer记录“恢复”,导致最终数据和预期不符。

  3. JPA方法的事务特性限制
    deleteById这类Repository方法在无外层事务时,执行完会立即提交但不会清空Session缓存。而save方法会触发Hibernate的脏检查,将缓存中的实体状态同步到数据库,此时缓存里的question仍持有已删除Answer的引用,必然导致数据不一致。

二、@Transactional注解的核心作用

  • 统一事务边界:给方法包裹一个完整事务,所有数据库操作(查询、删除、更新、插入)都在同一个事务内执行,保证操作的原子性——要么全部成功,要么全部回滚。
  • 管理持久化上下文:事务开启时创建一个Session,方法内所有实体都在该Session缓存中,所有操作同步到缓存后,直到事务提交时才一次性将所有变更刷入数据库,从根源避免缓存与数据库状态不一致的问题。
  • 事务传播控制:可通过参数(如propagation = Propagation.REQUIRES_NEW)配置事务传播行为,适配嵌套事务、独立事务等不同业务场景。
  • 自动回滚机制:默认情况下,方法抛出RuntimeException或Error时事务自动回滚;若需针对Checked Exception触发回滚,可通过rollbackFor参数自定义规则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 06:22:49