UnexpectedRollbackException: un análisis de escenario completo

Todo lo que sé sobre esta excepción es de la documentación de Spring y algunas publicaciones en foros con desarrolladores congelados que pegan enormes rastros de pila, y no hay respuestas.

De la documentación de Spring:

Lanzado cuando un intento de confirmar una transacción resultó en una reversión inesperada

Quiero entender de una vez por todas

  1. ¿Exactamente qué lo causa?

    • ¿Dónde ocurrió el retroceso? en el código del servidor de aplicaciones o en la base de datos?
    • ¿Fue causado por una excepción subyacente específica (por ejemplo, algo de java.sql. *)?
    • ¿Está relacionado con Hibernate? ¿Está relacionado con Spring Transaction Manager (no JTA en mi caso)?
  2. ¿Cómo evitarlo? ¿Hay alguna mejor práctica para evitarlo?

  3. ¿Cómo depurarlo? parece ser difícil de reproducir, ¿alguna forma comprobada de solucionarlo?
Respuesta 1

Encontré que esto respondía el resto de la pregunta: https://jira.springsource.org/browse/SPR-3452

Supongo que necesitamos diferenciar entre los ámbitos de transacción 'lógicos' y las transacciones 'físicas' aquí ...

Lo que PROPAGATION_REQUIRED crea es un alcance de transacción lógica para cada método al que se aplica. Cada uno de estos ámbitos de transacciones lógicas puede decidir individualmente el estado de solo reversión, con un ámbito de transacción externo que es lógicamente independiente del ámbito de transacción interno. Por supuesto, en caso de un comportamiento PROPAGATION_REQUIRED estándar, se asignarán a la misma transacción física. Por lo tanto, un marcador de reversión solo establecido en el alcance interno de la transacción afecta la posibilidad de la transacción externa de comprometerse realmente. Sin embargo, dado que el alcance de la transacción externa no decidió una reversión en sí misma, la reversión (activada silenciosamente por el alcance de la transacción interna) se produce de manera inesperada en ese nivel, razón por la cual se produce una UnexpectedRollbackException.

PROPAGATION_REQUIRES_NEW, por el contrario, utiliza una transacción completamente independiente para cada ámbito de transacción afectado. En ese caso, las transacciones físicas subyacentes serán diferentes y, por lo tanto, pueden comprometerse o revertirse independientemente, con una transacción externa no afectada por el estado de reversión de una transacción interna.

PROPAGATION_NESTED es diferente nuevamente en el sentido de que utiliza una única transacción física con múltiples puntos de rescate a los que puede revertir. Tales reversiones parciales permiten que un alcance de transacción interno active un retroceso para su alcance, con la transacción externa pudiendo continuar la transacción física a pesar de que algunas operaciones se han retrotraído. Por lo general, esto se asigna a los puntos de guardado JDBC, por lo que solo funcionará con transacciones de recursos JDBC (DataSourceTransactionManager de Spring).

Para completar la discusión: UnexpectedRollbackException también se puede generar sin que la aplicación haya establecido un marcador de retroceso solo. En cambio, la infraestructura de la transacción puede haber decidido que el único resultado posible es una reversión, debido a restricciones en el estado actual de la transacción. Esto es particularmente relevante con las transacciones XA.

Como sugerí anteriormente, lanzar una excepción en el alcance interno de la transacción, luego capturar esa excepción en el alcance externo y traducirla en una llamada silenciosa setRollbackOnly allí debería funcionar para su escenario. Una persona que llama de la transacción externa nunca verá una excepción entonces. Dado que solo se preocupa por tales retrocesos silenciosos debido a los requisitos especiales impuestos por una persona que llama, incluso diría que la solución arquitectónica correcta es usar excepciones dentro de la capa de servicio y traducir esas excepciones en retrocesos silenciosos en el nivel de la fachada de servicio (derecha antes de volver a esa llamada especial).

Dado que su problema posiblemente no sea solo acerca de las excepciones de reversión, sino más bien sobre cualquier excepción lanzada desde su capa de servicio, incluso podría usar retrocesos basados ​​en excepciones estándar en toda su capa de servicio, y luego capturar y registrar tales excepciones una vez que la transacción haya ya completado, en una fachada de servicio de adaptación que traduce las excepciones de su capa de servicio en estados de error específicos de la interfaz de usuario.

Juergen

Respuesta: 2

Estoy planeando realizar muchas eliminaciones del último personaje en StringBuilders. La solución para usar sb.setLength (sb.length () - 1); me parece bien. Sin embargo, dado que estas eliminaciones estarán en un bucle, ...

Tengo un complemento Maven que tiene un groupId, artifactId y una versión en su configuración. Quiero poder descargar ese artefacto desde los repositorios remotos y copiar el archivo en el proyecto. ...

Estoy ejecutando una aplicación de procesamiento de transmisión en un entorno LocalStream (clúster de parpadeo integrado). Procesé un conjunto de datos específico usando mi código con éxito varias veces. Quería volver a ejecutar el ...

Cuando realiza una solicitud de descanso con RestAssured, parece esperar una respuesta. Necesito hacer una solicitud POST en RestAssured y luego, mientras espera una respuesta, necesito hacer una solicitud GET. YO'...