Как предотвратить откат транзакции JPA? - PullRequest
9 голосов
/ 09 ноября 2009

Использованные методы:
1. Struts Action
2. Метод класса обслуживания (аннотируется @Transactional)
3. Звонок на веб-сервис Xfire

Все, включая Struts (DelegatingActionProxy) и транзакции, настроены с помощью Spring.

Постоянство выполняется с помощью JPA / Hibernate.

Иногда веб-служба генерирует непроверенное исключение. Я ловлю это исключение и выбрасываю проверенное исключение. Я не хочу, чтобы транзакция откатывалась, поскольку исключение веб-службы изменяет текущее состояние. Я аннотировал метод следующим образом:

@Transactional(noRollbackFor={XFireRuntimeException.class, Exception.class})
public ActionForward callWS(Order order, ....) throws Exception
  (...)
  OrderResult orderResult = null;

  try {
    orderResult = webService.order(product, user)
  } catch (XFireRuntimeException xfireRuntimeException) {
    order.setFailed(true);
    throw new WebServiceOrderFailed(order);
  } finally {
    persist(order);
  }
}

Я все еще получаю это исключение:

org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly

Когда я пытаюсь воспроизвести это с помощью junit, транзакция не помечается для отката, и все еще можно зафиксировать транзакцию.

Как заставить Spring не откатывать транзакцию?

Ответы [ 2 ]

9 голосов
/ 10 ноября 2009

Удалось создать контрольный пример для этой проблемы:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"file:web/WEB-INF/spring/applicationContext.xml",
        "file:web/WEB-INF/spring/services.xml"})
@Transactional
public class DoNotRollBackTest {
    @Autowired FakeService fakeService;

    @Test
    @Rollback(false)
    public void testRunXFireException() {
        fakeService.doSomeTransactionalStuff();
    }
}

FakeService:

@Service
public class FakeService {
    @Autowired private EcomService ecomService;
    @Autowired private WebService webService;

    @Transactional(noRollbackFor={XFireRuntimeException.class})
    public void doSomeTransactionalStuff() {
        Order order = ecomService.findOrderById(459);

        try {
            webService.letsThrowAnException();
        } catch (XFireRuntimeException e) {
            System.err.println("Caugh XFireRuntimeException:" + e.getMessage());
        }

        order.setBookingType(BookingType.CAR_BOOKING);
        ecomService.persist(order);
    }
}

WebService:

@Transactional(readOnly = true)
public class WebService {
    public void letsThrowAnException() {
        throw new XFireRuntimeException("test!");
    }
}

Это создаст исключение отката.

Затем я понял, что транзакция, вероятно, помечена как rollbackOnly в WebService.letsThrowAnException, поскольку WebService также является транзакционным. Я перешел к аннотации:

@Transactional(noRollbackFor={XFireRuntimeException.class})
    public void letsThrowAnException() {

Теперь транзакция не откатывается, и я могу зафиксировать изменения в Order.

3 голосов
/ 09 ноября 2009

Вы не должны бросать исключение, где Spring может видеть это. В этом случае вы не должны бросать WebServiceOrderFailed(). Решение состоит в том, чтобы разделить код на два метода. Первый метод выполняет обработку ошибок и возвращает исключение, внешний метод создает транзакцию.

[EDIT] Что касается noRollbackFor: попробуйте заменить Exception.class на WebServiceOrderFailed.class.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...