java - transaction - transacciones hibernate
Mejores prácticas para revertir transacciones en Spring 3/Hibernate (5)
Haciendo referencia a la documentación de Spring :
Cualquier RuntimeException activará la reversión, y cualquier excepción marcada no
Referencia a javapractices.com
Excepciones no verificadas:
- representan defectos en el programa (errores): a menudo los argumentos no válidos se pasan a un método no privado. Para citar de The Java Programming Language, de Gosling, Arnold y Holmes: "Las excepciones no controladas en tiempo de ejecución representan condiciones que, en general, reflejan errores en la lógica de su programa y no pueden recuperarse razonablemente en el tiempo de ejecución".
- son subclases de RuntimeException y, por lo general, se implementan con IllegalArgumentException, NullPointerException o IllegalStateException
- Un método no está obligado a establecer una política para las excepciones no verificadas lanzadas por su implementación (y casi siempre no lo hacen)
Excepciones comprobadas:
- representar condiciones no válidas en áreas fuera del control inmediato del programa (entrada de usuario no válida, problemas con la base de datos, cortes de red, archivos ausentes)
- son subclases de excepción
- Un método está obligado a establecer una política para todas las excepciones marcadas lanzadas por su implementación (ya sea pasar la excepción marcada más arriba en la pila o manejarla de alguna manera)
Si durante mi lógica de negocios descubro un problema y quiero revertir los cambios, ¿tengo que lanzar una nueva excepción RuntimeException? No es realmente una excepción RuntimeException (excepción no verificada) ya que lo he identificado en la lógica. O tal vez estoy malinterpretando estos conceptos?
Mi pregunta real , ¿cuáles son las mejores prácticas para revertir una transacción en mis métodos de servicio @Transactional?
Creo que es seguro decir que hay diferentes opiniones sobre qué excepciones deben verificarse. Por ejemplo, considere este extracto de Introducción al Marco de Primavera :
La jerarquía de excepciones de acceso a datos de Spring se basa en excepciones no verificadas (tiempo de ejecución). Después de haber trabajado con Spring en varios proyectos, estoy cada vez más convencido de que esta fue la decisión correcta.
Las excepciones de acceso a los datos no suelen ser recuperables. Por ejemplo, si no podemos conectarnos a la base de datos, es poco probable que un objeto comercial en particular pueda solucionar el problema. Una posible excepción son las infracciones de bloqueo optimista, pero no todas las aplicaciones utilizan el bloqueo optimista. Por lo general, es malo ser forzado a escribir código para detectar excepciones fatales que no se pueden manejar con sensatez. Permitir que se propaguen a manejadores de nivel superior como el servlet o el contenedor EJB suele ser más apropiado. Todas las excepciones de acceso a datos de Spring son subclases de la excepción DataAccessException, por lo que si elegimos capturar todas las excepciones de acceso a datos de Spring, podemos hacerlo fácilmente.
Tenga en cuenta que si queremos recuperarnos de una excepción de acceso a datos sin marcar, todavía podemos hacerlo. Podemos escribir código para manejar solo la condición recuperable. Por ejemplo, si consideramos que solo se puede recuperar una violación de bloqueo optimista, podemos escribir el código en un DAO Spring de la siguiente manera:
try {// do work} catch ((ex. OptimisticLockingFailureException) {// Estoy interesado en esto} Si se verificaron las excepciones de acceso a datos de Spring, tendríamos que escribir el siguiente código. Tenga en cuenta que podríamos optar por escribir esto de todos modos:
try {// do work} catch (captura ex. (OptimisticLockingFailureException) {// Estoy interesado en esta} captura (ex DataAccessException) {// Fatal; simplemente vuelva a lanzarlo} Una posible objeción al primer ejemplo, que el compilador no puede hacer cumplir con el manejo de la excepción potencialmente recuperable, se aplica también al segundo. Debido a que estamos obligados a detectar la excepción base (DataAccessException), el compilador no aplicará una verificación para una subclase (OptimisticLockingFailureException). Por lo tanto, el compilador nos obligaría a escribir código para manejar un problema irrecuperable, pero no nos proporcionaría ninguna ayuda para obligarnos a lidiar con el problema recuperable.
El uso de Spring de excepciones de acceso a datos no verificadas es consistente con el de muchos, probablemente, los marcos de persistencia más exitosos. (De hecho, fue inspirado en parte por JDO). JDBC es una de las pocas API de acceso a datos para usar excepciones comprobadas. TopLink y JDO, por ejemplo, usan excepciones no marcadas exclusivamente. Hibernate cambió de excepciones marcadas a no verificadas en la versión 3.
Las excepciones de acceso a los datos están claramente fuera del control inmediato del programa, por lo que, de acuerdo con las prácticas prácticas, se deben verificar. Pero las personas en la fuente de primavera difieren. Y confío en su juicio más que en las prácticas.
Por lo tanto, no veo nada de malo en lanzar una excepción no marcada para indicar que la transacción debe revertirse. Por supuesto, también puede usar excepciones marcadas y configurar el aspecto para revertirlas también. (ver la respuesta de Affe para más detalles)
Debería ser como
@Transactional
public void method () throws YourCustomException {
try{
//logic
}catch(Exception ex){
TransactionAspectSupport.currentTransactionStatus()
.setRollbackOnly();
throw(new YourCustomException(ex.getMessage()));
}
}
Encontré esta URL para manejar las excepciones marcadas y no controladas, administrar transacciones: no es el enfoque basado en anotaciones, pero es bueno.
O bien revertir programáticamente desde dentro:
@Transactional
public void commit() {
try {
// some business logic...
} catch (ConstraintViolationException e) {
// trigger rollback programmatically
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
o marque la excepción para revertir y manejarla desde la persona que llama:
@Transactional(rollBackFor = TransactionException.class)
public void commit() throws ConstraintViolationException{
try {
// some business logic...
} catch (ConstraintViolationException e) {
// handle the exception
// re-throw for rollback
new TransactionException(e);
}
}
public void doCommit(){
try {
commit()
} catch (TransactionException e){
// do nothing as already handled
}
}
Prefiero lo primero, ya que mantiene el código más simple, pero se discouraged acuerdo con los documentos de Spring:
La reversión programática está disponible en caso de que la necesite absolutamente, pero su uso va en contra de lograr una arquitectura limpia basada en POJO.
Si está utilizando excepciones marcadas, simplemente las agrega a la propiedad rollbackFor
de su anotación @Transactional
.
@Transactional(rollbackFor = { MyInvalidUserException.class, MyApplicationException.class })
public void method() throws MyInvalidUserException, MyApplicationException {
...
...
}
etc.
La respuesta de org.life.java también funciona bien. Es una decisión académica si desea combinar la gestión programática de transacciones en sus transacciones declarativas o mantenerla estrictamente declarativa.