当前位置: 首页 > 知识库问答 >
问题:

当方法被标记为noRollbackFor抛出异常时,@Transactional类中的意外RollbackException

秦鸿羽
2023-03-14

我有一个@Transactional标记的类FooServiceImpl,需要用从另一个@Transactional类BarServiceImpl获得的值填充一个对象FooDetails。

尝试从BarServiceImpl获取值时,会抛出Exception1。我想将FooDetails字段保留为空,该字段的setter抛出异常并捕获异常。即使某些setter抛出异常,所有其他字段也必须保持填充状态。

可悲的是,情况并非如此,因为事务已被标记为仅回滚,因为抛出了Exception1,尽管我明确地将该方法标记为“noRollbackFor=Exception1.class”并捕获了异常。

为什么会发生这种情况,我如何修复代码以填充设置器没有抛出异常的所有字段,并将设置器抛出异常的字段留空。这是导致行为的代码:

@Service
@Transactional
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TransactionalService {
    // The name of the service
    String value() default "";
}

public class FooDetails {
    private String fieldA;
    private String fieldB;

    // getters and setters
    ...
}

@TransactionalService 
public class BarServiceImpl implements BarService {
    @Override
    public String getFieldB() throws Exception1 {
        ...
        try {
            ...
        } catch (Exception2 e2) {
            ...
            throw new Exception1();
        } catch (Exception3 e3) {
            ...
            throw new Exception1();
        }
    }
}

@TransactionalService 
public class FooServiceImpl implements FooService {
    ...
    ...

    @Autowired BarService barService;

    @Override
    @Transactional(noRollbackFor = {Exception1.class, Exception2.class, Exception3.class})
    public FooDetails getFooDetails(Long fooId) {
        Foo foo = fooDao.get(fooId);
        if (foo == null) {
            return null;
        }

        FooDetails fooDetails = new FooDetails();

        // getAFooDetails() has no exceptions to throw => ok
        fooDetails.setFieldA(getAFooDetails());

        // barService.getFieldB() throws an Exception1 => results in 
        // "org.springframework.transaction.UnexpectedRollbackException:
        //           Transaction rolled back because it has been marked as rollback-only" => all foo fields left empty
        try {
            fooDetails.setFieldB(barService.getFieldB());
        } catch (Exception1 e) {
            LOG.info(e.getMessage());
        }        
    }
}

共有1个答案

陈斌
2023-03-14

您正在使用两个逻辑事务作用域:外部事务作用域(在FooServiceImpl中)和内部事务作用域(在BarServiceImpl中)。两者都属于同一个物理事务。

因为您说Exception1是一个已检查的异常,所以在BarServiceImpl中抛出它不会导致任何回滚(直到显式声明)。在FooServiceImpl中捕获它也不会导致任何回滚。

通常,如果内部事务范围(此处:您的BarServiceImpl.getFieldB)触发回滚,则会抛出一个UnoutedRollbackException。当外部事务范围的调用方法(此处:您的FooServiceImpl.getFoo细节)正常返回时,Spring的AbstractPlatformTransactionManager将尝试提交。此提交失败,因为其他人已经将其标记为“rollbackOnly”(由内部事务范围完成)。然后它会将一个UnoutedRollbackException抛回给FooServiceImpl.getFoo细节的调用者。

所以,你的BarServiceImpl。getFieldB一定以某种方式引发了未经检查的异常(与异常1不同)

您在此处未显示的代码中的其他地方使用了一个单一的物理事务,并且这段代码确实通过未经检查的异常触发了回滚。

 类似资料:
  • 问题内容: 我有这样的方法: 我想抛出一个内。编译器不允许我这样做,因为不允许将我的方法扔在那里。但是我需要抛出一个的子类来进行测试 (我不能抛出Unchecked)。显然这是一个hack,但我需要进行测试。我尝试过EasyMock,但它也不允许我这样做。任何想法如何做到这一点? 谢谢,肖恩·阮 问题答案: 方法1: Alexey Ragozin的这篇文章介绍了如何使用泛型技巧引发未声明的检查异常

  • 抛出异常有什么意义?例如,我偶然发现: 但是当你不扔零点的时候,你也会得到一个零点?我经常看到这个,我想知道这是否是一个学习的好习惯?

  • 当我从EJB调用一个方法时,我遇到了一个奇怪的异常。EJB的方法只是参考BD并返回一个VO。当我从一个独立的客户机调用该方法时,所有这些都能很好地工作,但是当我从同一台服务器、从另一个EJB调用该方法时,它返回:org。天啊。科尔巴。未知:重新引发挂起的异常时发现意外异常:(.VOObject)vmcid:IBM次要代码:EA5已完成:可能 服务器是WebSphereApplicationServ

  • 问题内容: 我试图在Netbeans中重构一个大型程序,但我有点迷茫。我从来没有非常模块化,但是现在通过实际学习如何做到这一点来尝试纠正这种情况,并在将来纠正这种情况。不幸的是,我在将某些教程翻译成我的程序时遇到了麻烦。所以我希望这里有人可以帮忙。目前,我正在尝试分解一部分采用特定格式的文件并制成表格的代码。我知道我需要创建一个类并使用它来创建表对象,但是我不确定如何做。我有一个主文件,用于获取文

  • 我有一个Spring/JPA配置,其中Hibernate作为持久性提供者。但是,我不明白为什么在没有打开事务的情况下对以下DAO代码调用save()时没有抛出TransactionRequiredException(DAO/服务中没有@Transactional): 正如预期的那样,实体没有保存,但为什么没有引发异常?持久化的javadoc表示,持久化()应该抛出一个“Transaction必需的

  • 我想问一下这种行为的原因,因为在运行Spring方法/类时,我似乎不完全理解Hibernate中的和之间的区别。 下面的代码应该回滚DB操作,但它没有回滚(整个类注释为): 以下代码在引发异常时按预期回滚: save()方法来自类,因此它的代码是: 该实体是一个已存在的实体,因此我理解它正在执行。根据JPA规范: find方法(前提是在没有锁的情况下调用它或使用LockModeType.None调