2015-12-14 2 views
3

Учитывая этот пример кода:Hibernate/Spring - Откат транзакции в рамках транзакции

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 должен продолжаться, это означает, что каждый из них должен заканчиваться.
  3. Если исключение происходит внутри myTransactionalMethod, но не в processItem(anItem), myTransactionalMethod должен полностью откатиться.

Есть ли решение, которое не включает управление транзакциями вручную (без аннотаций) ?.

Редактировать: Я думал об использовании @Transactional(PROPAGATION=REQUIRES_NEW), не знаю, будет ли он работать в одном и том же компоненте.

ответ

7

Это распространенное недоразумение. Весенние транзакции реализуются через прокси. Прокси - обертка вокруг вашего класса. Вы получаете доступ к методу processItem из того же класса, то есть вы не проходите через прокси-сервер, поэтому вы не получаете никаких транзакций. I explained the mechanism in this answer несколько лет назад.

Решение: вам нужны две отдельные весенние бобы, если вы хотите вложенные транзакции, и то и другое должно быть проксировано @Transactional.

+0

Спасибо, вот что я думал. – dantebarba

0

Похоже, что для транзакции NESTED. Операция NESTED запускает субтранзакцию с внешней транзакцией с точкой сохранения, позволяя ей откатиться к этой точке сохранения. Так как это вложенные транзакции, которые они совершили в конце внешней трансакции.

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 - [http://stackoverflow.com/questions/3423972/spring-transaction-method-call-by-the-method-within-the-same-class-does-not-wo][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); 
    } 
    } 

Обратите внимание, что я еще не пробовал этот ниже код, если это поможет. Возможно, вам придется настроить его, если необходимо. На самом деле, я бы хотел, чтобы этот код попробовал сам в ближайшее время.

+0

Я согласен с общей идеей решения выше, но код не работает. См. [This] (http://stackoverflow.com/a/9020408/2504224). Чтобы решение работало, вам нужно переместить 'processItem' в другой класс – geoand

+0

@geoand Это правда. Либо переместите его на другой класс (это то, что я имел в виду под рефактором, я должен был быть более конкретным), либо использовать aspectJ для обработки транзакций, как [link] (http://stackoverflow.com/questions/3423972/spring-transaction- метод-call-by-the-method-in-same-class-does-not-wo), если я не интерпретирую ссылку неправильно. –

+1

Это правильно. Использование 'AspectJ' для решения такого простого случая является излишним, я бы сказал, – geoand

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