10

在我的春天启动的项目,我已经实现了下列服务方法:春嵌套事务

@Transactional 
public boolean validateBoard(Board board) { 
    boolean result = false; 
    if (inProgress(board)) { 
     if (!canPlayWithCurrentBoard(board)) { 
      update(board, new Date(), Board.AFK); 
      throw new InvalidStateException(ErrorMessage.BOARD_TIMEOUT_REACHED); 
     } 
     if (!canSelectCards(board)) { 
      update(board, new Date(), Board.COMPLETED); 
      throw new InvalidStateException(ErrorMessage.ALL_BOARD_CARDS_ALREADY_SELECTED); 
     } 
     result = true; 
    } 
    return result; 
} 

这个方法我用另一种服务方法被称为update内:

@Transactional(propagation = Propagation.REQUIRES_NEW) 
public Board update(Board board, Date finishedDate, Integer status) { 
    board.setStatus(status); 
    board.setFinishedDate(finishedDate); 

    return boardRepository.save(board); 
} 

我需要提交在update方法中独立于在validateBoard方法中启动的所有者事务而更改数据库。现在,任何异常都会回滚。

即使与@Transactional(propagation = Propagation.REQUIRES_NEW)它不起作用。

如何正确使用Spring做到这一点,并允许嵌套事务?

+4

显然你正在调用同一个类中的一个方法,所以Spring不能拦截这个调用并应用一个事务代理('REQUIRES_NEW'传播被忽略)。您应该将'update'方法迁移到另一个Spring bean, –

+0

谢谢,现在一切按预期工作 – alexanoid

回答

8

本文档介绍您的问题 - http://docs.spring.io/autorepo/docs/spring/current/spring-framework-reference/html/transaction.html

在代理模式(这是默认值),只有外部方法调用通过代理来在被截获。这意味着即使被调用的方法标记为@Transactional,实际上,自调用目标对象内的方法调用目标对象的另一个方法也不会导致实际的事务处理。此外,代理必须完全初始化以提供预期的行为,因此您不应该在初始化代码中依赖此功能,即@PostConstruct。

然而,切换到AspectJ的模式

1

如果从同一类的某些方法调用Spring事务基础结构,您的交易注释update方法将不被视为。欲了解Spring交易基础设施如何运作的更多信息,请参阅this

0

你的问题是一个方法的来自同一proxy.It的自我调用内的另一个方法调用的选项。 在你的情况下,你可以很容易地修复它,而无需在另一个服务中移动一个方法(为什么你需要创建另一个服务只是为了避免自我调用而将一些方法从一个服务移动到另一个服务?),只需要调用第二个方法不是直接来自当前类,而是来自spring容器。在这种情况下,您可以使用事务而不是自我调用来调用代理第二方法。

当您需要自我调用时,此原则对于任何代理对象都很有用,而不仅仅是事务代理。

@Service 
class SomeService ..... { 
    -->> @Autorired 
    -->> private ApplicationContext context; 
    -->> //or with implementing ApplicationContextAware 

    @Transactional(any propagation , it's not important in this case) 
    public boolean methodOne(SomeObject object) { 
     ....... 
     -->> here you get a proxy from context and call a method from this proxy 
     -->>context.getBean(SomeService.class). 
      methodTwo(object); 
     ...... 
    } 

    @Transactional(any propagation , it's not important in this case)public boolean 
    methodTwo(SomeObject object) { 
    ....... 
    } 
} 

当你调用context.getBean(SomeService.class).methodTwo(object);容器返回代理对象,并在此代理可以调用​​methodTwo(...)与交易。