sec mvc form spring spring-boot spring-data spring-data-jpa spring-transactions

spring - mvc - Transacciones anidadas de primavera



spring form form action (5)

Esta documentación cubre su problema: https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/data-access.html#transaction-declarative-annotations

En el modo proxy (que es el valor predeterminado), solo se interceptan las llamadas a métodos externos que llegan a través del proxy. Esto significa que la autoinvocación, en efecto, un método dentro del objeto de destino que llama a otro método del objeto de destino, no conducirá a una transacción real en tiempo de ejecución, incluso si el método invocado está marcado con @Transactional. Además, el proxy debe estar completamente inicializado para proporcionar el comportamiento esperado, por lo que no debe confiar en esta función en su código de inicialización, es decir, @PostConstruct.

Sin embargo, hay una opción para cambiar al modo AspectJ

En mi proyecto Spring Boot he implementado el siguiente método de servicio:

@Transactional public boolean validateBoard(Board board) { boolean result = false; if (inProgress(board)) { if (!canPlayWithCurrentBoard(board)) { update(board, new Date(), Board.AFK); throw new InvalidStateException(ErrorMessage.BOARD_TIMEOUT_REACHED); } if (!canSelectCards(board)) { update(board, new Date(), Board.COMPLETED); throw new InvalidStateException(ErrorMessage.ALL_BOARD_CARDS_ALREADY_SELECTED); } result = true; } return result; }

Dentro de este método utilizo otro método de servicio que se llama update :

@Transactional(propagation = Propagation.REQUIRES_NEW) public Board update(Board board, Date finishedDate, Integer status) { board.setStatus(status); board.setFinishedDate(finishedDate); return boardRepository.save(board); }

Necesito confirmar los cambios en la base de datos en el método de update independientemente de la transacción del propietario que se inicia en el método validateBoard . En este momento, cualquier cambio está retrocediendo en caso de cualquier excepción.

Incluso con @Transactional(propagation = Propagation.REQUIRES_NEW) no funciona.

¿Cómo hacer esto correctamente con Spring y permitir transacciones anidadas?


La anotación de transacción en el método de update no será considerada por la infraestructura de transacción de Spring si se llama desde algún método de la misma clase. Para obtener más información sobre cómo funciona la infraestructura de transacciones de Spring, consulte this .


La regla básica básica en términos de transacciones anidadas es que dependen completamente de la base de datos subyacente, es decir, el soporte para transacciones anidadas y su manejo depende de la base de datos y varía con ellas. En algunas bases de datos, los cambios realizados por la transacción anidada no son vistos por la transacción ''host'' hasta que se confirma la transacción anidada. Esto se puede lograr usando el aislamiento de transacción en @Transactional (isolation = "")

Debe identificar el lugar en su código desde donde se lanza una excepción, es decir, desde el método principal: "validateBoard" o desde el método secundario: "actualizar".

Su fragmento de código muestra que está lanzando explícitamente las excepciones.

DEBES SABER::

En su configuración predeterminada, el código de la infraestructura de transacción de Spring Framework solo marca una transacción para la reversión en el caso de tiempo de ejecución, excepciones no verificadas; es decir, cuando la excepción lanzada es una instancia o subclase de RuntimeException.

Pero @Transactional nunca revierte una transacción por ninguna excepción verificada.

Así, Spring te permite definir.

  • Excepción por la cual la transacción debe ser retrotraída
  • Excepción para la cual la transacción no debe ser retrotraída

Intente anotar su método secundario: actualice con @Transactional (no-rollback-for = "ExceptionName") o su método principal.


Su problema es la llamada de un método desde otro método dentro del mismo proxy. Es la autoinvocación. En su caso, puede arreglarlo fácilmente sin mover un método dentro de otro servicio (¿por qué necesita crear otro servicio solo para mover un método de un servicio a otro solo para evitar la auto-invocación?), Solo para llamar al segundo método no directamente de la clase actual, sino del contenedor de primavera. En este caso, usted llama al segundo método proxy con una transacción no con autoinvocación.

Este principio es útil para cualquier objeto proxy cuando necesita autoinvocación, no solo un proxy transaccional.

@Service class SomeService ..... { -->> @Autorired -->> private ApplicationContext context; -->> //or with implementing ApplicationContextAware @Transactional(any propagation , it''s not important in this case) public boolean methodOne(SomeObject object) { ....... -->> here you get a proxy from context and call a method from this proxy -->>context.getBean(SomeService.class). methodTwo(object); ...... } @Transactional(any propagation , it''s not important in this case)public boolean methodTwo(SomeObject object) { ....... } }

cuando llamas a context.getBean(SomeService.class).methodTwo(object); el contenedor devuelve el objeto proxy y en este proxy puede llamar a methodTwo(...) con la transacción.


Usando "self" jnject parttner puede resolver este problema.

código de muestra como abajo:

@Service @Transactional public class YourService { ... your member @Autowired private YourService self; //inject proxy as instance member variable ; @Transactional(propagation= Propagation.REQUIRES_NEW) public void methodFoo() { //... } public void methodBar() { //call self.methodFoo() rather than this.methodFoo() self.methodFoo(); } }

El punto es usar "yo" en lugar de "esto".