2009-11-09 22 views
9

métodos invocados: método de clase
1. Struts Acción
2. Servicio (anotado por @Transactional)
3. Xfire llamada de servicio web¿Cómo evitar que JPA retrase la transacción?

todo, incluyendo puntales (DelegatingActionProxy) y las transacciones se configura con la primavera.

La persistencia se realiza con JPA/Hibernate.

A veces, el servicio web lanzará una excepción sin marcar. Capté esta excepción y lancé una excepción marcada. No quiero que la transacción se retrotraiga porque la excepción del servicio web cambia el estado actual. He anotado el método de la siguiente manera:

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

todavía tengo esta excepción:

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

Cuando trato de reproducir esto con JUnit, la transacción no está marcado para el rodillo hacia atrás y todavía es posible para comprometer la transacción.

¿Cómo hago que Spring no deshaga la transacción?

Respuesta

7

conseguido crear un caso de prueba para este problema:

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

Esto volverá a crear el retroceso en excepciones.

Luego me di cuenta de que la transacción probablemente se está marcando como rollbackOnly en WebService.letsThrowAnException ya que WebService también es transaccional. Me moví a la anotación:

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

Ahora la transacción no se está retrotrayendo y puedo confirmar los cambios en el pedido.

3

No debe lanzar una excepción donde Spring pueda verla. En este caso, no debe lanzar WebServiceOrderFailed(). La solución es dividir el código en dos métodos. El primer método maneja el error y devuelve la excepción, el método externo crea la transacción.

[EDIT] En cuanto a noRollbackFor: Intente reemplazar Exception.class con WebServiceOrderFailed.class.

+1

Esto es incorrecto. 'noRollbackFor' comprueba la clase de excepción especificada y todas sus subclases: http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/transaction/annotation/Transactional.html#noRollbackFor() Además , por defecto, las excepciones marcadas NO activarán la reversión: http://static.springsource.org/spring/docs/2.5.x/reference/transaction.html#transaction-declarative-attransactional-settings – ChssPly76

+1

Esto no explica por qué El código anterior vuelve a cargarse en 'WebServiceOrderFailed'. –

+0

Supongo que WebServiceOrderFailed es una RuntimeException y el código anterior ('noRollbackFor = {..., Exception.class} ') no puede tener ningún efecto ya que Exception se maneja especialmente (de lo contrario, el código de herencia también ignoraría RuntimeException ya que amplía Exception). –

Cuestiones relacionadas