복붙노트

[SPRING] JPA가 트랜잭션을 롤백하는 것을 방지하는 방법은 무엇입니까?

SPRING

JPA가 트랜잭션을 롤백하는 것을 방지하는 방법은 무엇입니까?

호출 된 메소드 : 1. Struts 액션 2. 서비스 클래스 메소드 (@Transactional에 의해 주석 됨) 3. Xfire 웹 서비스 호출

Struts (DelegatingActionProxy) 및 트랜잭션을 포함한 모든 것이 Spring으로 구성됩니다.

영속성은 JPA / Hibernate로 수행됩니다.

때로는 webservice가 검사되지 않은 예외를 throw합니다. 이 예외를 catch하고 확인 된 예외를 throw합니다. 웹 서비스 예외가 현재 상태를 변경하기 때문에 트랜잭션이 롤백되는 것을 원하지 않습니다. 나는 다음과 같은 방법으로 주석을 달았다.

@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으로 이것을 재현하려 할 때, 트랜잭션은 롤백으로 표시되지 않고 트랜잭션을 커밋 할 수 있습니다.

스프링이 트랜잭션을 롤백하지 않게하려면 어떻게해야합니까?

해결법

  1. ==============================

    1.이 문제에 대한 테스트 케이스를 작성하려면 다음과 같이 관리해야합니다.

    이 문제에 대한 테스트 케이스를 작성하려면 다음과 같이 관리해야합니다.

    @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!");
        }
    }
    

    이렇게하면 롤백 예외가 다시 작성됩니다.

    그런 다음 WebService가 트랜잭션이기 때문에 트랜잭션이 WebService.letsThrowAnException에서 rollbackOnly로 표시되고 있음을 알았습니다. 특수 효과로 옮겼습니다.

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

    이제 트랜잭션이 롤백되지 않고 주문 변경 사항을 커밋 할 수 있습니다.

  2. ==============================

    2.Spring이 볼 수있는 곳에 예외를 던져서는 안된다. 이 경우 WebServiceOrderFailed ()를 throw하면 안됩니다. 해결책은 코드를 두 가지 방법으로 분리하는 것입니다. 첫 번째 메서드는 오류 처리를 수행하고 예외를 반환하고 외부 메서드는 트랜잭션을 만듭니다.

    Spring이 볼 수있는 곳에 예외를 던져서는 안된다. 이 경우 WebServiceOrderFailed ()를 throw하면 안됩니다. 해결책은 코드를 두 가지 방법으로 분리하는 것입니다. 첫 번째 메서드는 오류 처리를 수행하고 예외를 반환하고 외부 메서드는 트랜잭션을 만듭니다.

    [편집] noRollbackFor에 관해서는 : Exception.class를 WebServiceOrderFailed.class로 대체하십시오.

  3. from https://stackoverflow.com/questions/1701750/how-to-prevent-jpa-from-rolling-back-transaction by cc-by-sa and MIT license