Подтвердить что ты не робот

Странное поведение с @Transactional (распространение = Propagation.REQUIRES_NEW)

Вот моя проблема:

Я запускаю пакет в приложении Java EE/ Spring/Hibernate. Эта партия вызывает method1. Этот метод вызывает method2, который может бросать UserException (класс, расширяющий RuntimeException). Вот как это выглядит:

@Transactional
public class BatchService implements IBatchService {
 @Transactional(propagation=Propagation.REQUIRES_NEW)
 public User method2(User user) {
   // Processing, which can throw a RuntimeException
 }

 public void method1() {
   // ...
   try {
     this.method2(user);
   } catch (UserException e) {
     // ...
   }
   // ...
 }
}

Исключением считается, что выполнение продолжается, но в конце method1, когда транзакция закрыта, генерируется исключение RollbackException.

Вот трассировка стека:

org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly
    at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:476)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at $Proxy128.method1(Unknown Source)
at batch.BatchController.method1(BatchController.java:202)

Если method2 не выбрасывает это исключение, оно работает хорошо.

Что я пробовал:

  • Настройка @Transactional(noRollbackFor={UserException.class})) на method1
  • Попробуйте и поймайте method2

Но это ничего не изменило.

Поскольку исключение выбрано в другой транзакции, где произошел откат, я не понимаю, почему он не работает. Я посмотрел на это: Jpa transaction javax.persistence.RollbackException: транзакция отмечена как rollbackOnly, но мне это действительно не помогло.

Я буду очень благодарен, если кто-то подскажет мне.

Обновление

Я сделал это, установив propagation=Propagation.REQUIRES_NEW на метод, называемый method2 (который фактически является тем, который отправляет исключение). Этот метод определен в классе, очень похожем на my BatchService. Поэтому я не понимаю, почему он работает на этом уровне, а не на method2.

  • Я установил method2 как общедоступный, поскольку аннотация @Transactional не учитывается, если метод является закрытым, как сказано в документации:

Аннотация @Transactional может быть помещена перед интерфейсом определение, метод на интерфейсе, определение класса или общедоступный метод в классе.

  • Я также попытался использовать Exception вместо RuntimeException (как это более подходит), но он ничего не изменил.

Даже если он работает, вопрос остается открытым, поскольку он имеет странное поведение, и я хотел бы понять, почему это не так, как должно быть.

4b9b3361

Ответ 1

Spring транзакции по умолчанию работают, обертывая Spring bean прокси-сервером, который обрабатывает транзакцию и исключения. Когда вы вызываете method2() из method1(), вы полностью обходите этот прокси-сервер, поэтому он не может начать новую транзакцию, и вы фактически вызываете method2() из той же транзакции, что и открытая вызовом method1().

Наоборот, когда вы вызываете метод другого введенного bean из method1(), вы фактически вызываете метод на транзакционном прокси. Поэтому, если этот чужой метод отмечен REQUIRES_NEW, прокси-сервер запускает новую транзакцию, и вы можете поймать исключение в method1() и возобновить внешнюю транзакцию.

Это описано в документации.