10

В моей загрузки проекта Spring Я осуществил следующий метод обслуживания:пружинные вложенные транзакции

@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

Ваш комментарий к транзакции в методе update не будет рассматриваться инфраструктурой транзакций Spring, если вызывается из какого-либо метода того же класса. Чтобы узнать больше о том, как работает инфраструктура Spring, см. this.

0

Вашей проблема вызов метода из другого метода внутри собственного вызова того же proxy.It в. В вашем случае вы можете легко исправить это, не перемещая метод внутри другой службы (зачем вам нужно создать другую службу только для перемещения какого-либо метода из одной службы в другую только для того, чтобы избежать самозапускания?), Просто для вызова второго метод не непосредственно из текущего класса, а из весеннего контейнера. В этом случае вы вызываете второй метод-посредник с транзакцией, а не с помощью self-invocatio.

Этот принцип полезен для любого прокси-объекта, когда вам нужен самозапуск, а не только прокси-сервер транзакции.

@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(...) с транзакцией.

Смежные вопросы