Hibernate/Spring - 在事务中回滚一个事务

4

有了这个示例代码:

public class MyServiceImpl implements MyService {
    @Transactional
    public void myTransactionalMethod() {
        List<Item> itemList = itemService.findItems();
        for (Item anItem : itemList) {
            try {
                processItem(anItem);
            catch (Exception e) {
                // dont rollback here 
                // rollback just one item
            }     
        }

    }

    @Transactional
    public void processItem(Item anItem) { 
        anItem.setSomething(new Something);
        anItem.applyBehaviour();
        itemService.save(anItem);
    }
}

这里是我想要实现的目标:
  1. 只有在processItem(anItem);内部发生异常时才会回滚。
  2. 如果出现异常,myTransactionalMethod应该继续执行,这意味着for-each应该结束。
  3. 如果myTransactionalMethod内部发生异常但不是在processItem(anItem)中,myTransactionalMethod应该完全回滚。
是否有一种解决方案,无需手动管理事务(不使用注释)?
编辑:我正在考虑使用@Transactional(PROPAGATION=REQUIRES_NEW),不知道它是否适用于同一个bean内。
2个回答

7
这是一个常见的误解。Spring事务通过代理实现。代理是对你的类进行包装的包装器。当您从同一类中访问`processItem`方法时,即没有经过代理,因此您不会获得任何事务。多年前我在这个答案中详细解释了机制。 解决方案:如果您想要嵌套事务,则需要两个单独的Spring bean,它们都必须由`@Transactional`进行代理。

谢谢,这正是我想的。 - dantebarba

0

看起来这是一个嵌套事务的情况。嵌套事务在外部事务中使用保存点启动子事务,允许它回滚到该保存点。由于这是嵌套事务,它们会在外部事务结束时提交。

    public class MyServiceImpl implements MyService {
    @Transactional
    public void myTransactionalMethod() {
        List<Item> itemList = itemService.findItems();
        for (Item anItem : itemList) {
            try {
               // If you want to call this method directly configure your transaction use to aspectJ for transaction handling or refactor the code. Refer - [https://dev59.com/e3A75IYBdhLWcg3wMmCo][1]
                processItem(anItem);
            catch (Exception e) {
                // dont rollback here 
                // rollback just one item
            }     
        }

    }

    @Transactional(PROPAGATION = PROPAGATION.NESTED)
    // Throw some runtime exception to rollback or some checkedException with rollbackFor attribute set in the above annotation
    public void processItem(Item anItem) { 
        anItem.setSomething(new Something);
        anItem.applyBehaviour();
        itemService.save(anItem);
    }
  }

请注意,我还没有尝试过下面的代码,看看它是否有帮助。如果需要的话,您可能需要进行微调。事实上,我很快就想自己尝试一下这段代码。

@geoand 这是真的。要么将其移动到另一个类中(这就是我所说的重构,我应该更具体),要么根据链接使用AspectJ进行事务处理,除非我对链接的解释有误。 - Madhusudana Reddy Sunnapu
1
没错。我认为使用 AspectJ 来解决这样一个简单的问题有些过度设计了。 - geoand
看起来,我们甚至可以在 for-each 循环中使用 AopContext.getCurrentProxy() 并调用 processItem(...)。但是为此,我们需要告诉 Spring 暴露当前代理。 - Madhusudana Reddy Sunnapu
1
@dantebarba 我同意这不是一个简单的更改,所以我看到的其他选项是将 processItem 移动到新类中,或者使用 AopContext.getCurrentProxy(),或者让 MyServiceImpl 实现 ApplicationContextAware 并在 for-each 循环中从 applicationContext 获取 MyServiceImpl(前提是它是原型 bean)。 - Madhusudana Reddy Sunnapu
@MadhusudanaReddySunnapu 我喜欢这个,看起来更简单。 - dantebarba
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接