Как предотвратить откат транзакции JPA?
Вызываемые методы:
1. Распорки Действия
2. Метод класса обслуживания (с аннотацией @Transactional)
3. Вызов веб-сервиса Xfire
Все, включая распорки (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 ответов:
Удалось создать тестовый случай для этой задачи:
@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); } }Веб-сервис:
@Transactional(readOnly = true) public class WebService { public void letsThrowAnException() { throw new XFireRuntimeException("test!"); } }Это создаст исключение отката.
Затем я понял, что транзакция, вероятно, помечается как rollbackOnly в WebService.letsThrowAnException, так как WebService также является транзакционным. Я перешел к аннотации:
@Transactional(noRollbackFor={XFireRuntimeException.class}) public void letsThrowAnException() {Теперь транзакция не откатывается, и я могу зафиксировать изменения в заказе.
Вы не должны бросать исключение там, где весна может его видеть. В этом случае вы не должны бросать
WebServiceOrderFailed(). Решение состоит в том, чтобы разделить код на два метода. Первый метод выполняет обработку ошибок и возвращает исключение, внешний метод создает транзакцию.[EDIT] как для
noRollbackFor: Попробуйте заменитьException.classнаWebServiceOrderFailed.class.
Comments