Prefacio
Spring especifica 7 tipos de comportamientos de propagación de transacciones en la interfaz de definición de transacciones. El comportamiento de propagación de transacciones es una característica de mejora de la transacción exclusiva del marco de Spring, y no pertenece al comportamiento de la base de datos del proveedor real de transacciones. Esta es una caja de herramientas poderosa que Spring nos proporciona, y el uso de líneas de propagación de transacciones puede proporcionar muchas comodidades para nuestros esfuerzos de desarrollo. Pero la gente tiene muchos malentendidos al respecto, y debe haber escuchado el rumor de que "el negocio del método de servicio es mejor para no ser anidado". Para usar las herramientas correctamente, primero debe comprender las herramientas. Este artículo presenta los siete comportamientos de propagación de transacciones en detalle y presenta los principales ejemplos de código del contenido.
Conceptos básicos
1. ¿Qué es el comportamiento de comunicación de transacciones?
El comportamiento de propagación de transacciones se usa para describir cómo se propagan las transacciones cuando los métodos modificados por un cierto comportamiento de propagación de transacciones se anidan en otro método.
Use pseudocódigo para explicar:
public void MethodA () {Methodb (); // dosomething} @Transaction (propagation = xxx) public void Methodb () {// Dosomething} methodA() en el código llama methodB() en anidados, y el comportamiento de propagación de transacciones de methodB() está determinado por @Transaction(Propagation=XXX) . Cabe señalar aquí que methodA() no inicia la transacción, y el método para modificar un cierto comportamiento de propagación de la transacción no tiene que llamarse en el método periférico para iniciar la transacción.
2. Siete comportamientos de propagación de transacciones en primavera
| Tipo de comportamiento de propagación de transacciones | ilustrar |
|---|---|
| Propagation_required | Si no hay transacción en la actualidad, cree una nueva transacción, y si ya hay una transacción, agrégala a la transacción. Esta es la elección más común. |
| Propagation_supports | Admite la transacción actual, y si actualmente no hay transacción, se ejecutará de manera no transaccional. |
| Propagation_mandatory | Use la transacción actual y arroje una excepción si actualmente no hay transacción. |
| Propagation_requires_new | Crear una nueva transacción. Si la transacción existe actualmente, suspenda la transacción actual. |
| Propagation_not_supported | Ejecutar operaciones de manera no transaccional, y si actualmente existe una transacción, la transacción actual está suspendida. |
| Propagation_Never | Se ejecuta de manera no transaccional y lanza una excepción si actualmente existe una transacción. |
| Propagation_nested | Si actualmente existe una transacción, se ejecuta dentro de una transacción anidada. Si actualmente no hay transacción, realice una operación similar a la propagation_required. |
La definición es muy simple y fácil de entender. Vamos a la sección de prueba de código para verificar si nuestra comprensión es correcta.
Verificación de código
El código en este artículo se presenta en dos capas en una estructura tradicional de tres capas, a saber, el servicio y la capa DAO. La primavera es responsable de la inyección de dependencia y el manejo de la transacción de anotación. La capa DAO es implementada por MyBatis. También puede usar cualquier método favorito, como Hibernate, JPA, JDBCTemplate, etc. La base de datos utiliza la base de datos MySQL, y también puede usar cualquier base de datos habilitada para transacciones, que no afectará los resultados de la verificación.
Primero creamos dos tablas en la base de datos:
usuario1
Crear tabla `user1` (` id` entero sin firmar no nulo automo_incement, `name` varchar (45) no nulo predeterminado '', clave primaria (` id`)) motor = innoDB;
usuario2
Crear tabla `user2` (` id` entero sin firmar no nulo automano_incement, `name` varchar (45) no nula predeterminado '', clave primaria (` id`)) motor = innoDB;
Luego escriba el código de capa de frijol y dao correspondiente:
Usuario1
User de clase pública1 {ID de entero privado; nombre de cadena privada; // se omiten y establecen métodos ...}Usuario2
User de clase pública2 {ID de entero privado; nombre de cadena privada; // se omiten y establecen métodos ...}Usuario1mapper
interfaz pública user1mapper {int Insert (user1 registro); User1 selectByPrimaryKey (ID de entero); // se omiten otros métodos ...}User2mapper
interfaz pública user2mapper {int Insert (user2 registro); User2 selectbyPrimaryKey (ID de entero); // se omiten otros métodos ...}Finalmente, el código de verificación específico es implementado por la capa de servicio, y lo enumeraremos en las siguientes situaciones.
1.propagation_required
Agregamos Propagation.REQUIRED Atributos requeridos a los métodos correspondientes de user1service y user2service.
Método de servicio de usuario1:
@ServicePublic Class User1ServiceImpl implementa User1Service {// omitir otros ... @Override @Transactional (propagation = propagation.Required) public void addRecued (user1 user) {user1mapper.insert (user); }}Método user2Service:
@ServicePublic Class User2ServiceImpl implementa User2Service {// omitir a otros ... @Override @TransactionAL (propagation = propagation.Required) public void addRecued (user2 user) {user2mapper.insert (user); } @Override @transactional (propagation = propagation.required) public void addrequiredException (user2 user) {user2mapper.insert (user); tirar nueva runtimeException (); }} 1.1 Escena 1
Este método periférico de escenario no habilita las transacciones.
Método de verificación 1:
@Override public void Nottransaction_exception_required_required () {user1 user1 = new User1 (); user1.setName ("Zhang San"); user1service.addrequired (user1); User2 user2 = new User2 (); user2.setName ("li si"); user2service.addRecued (user2); tirar nueva runtimeException (); }Método de verificación 2:
@Override public void Nottransaction_required_required_exception () {user1 user1 = new User1 (); user1.setName ("Zhang San"); user1service.addrequired (user1); User2 user2 = new User2 (); user2.setName ("li si"); user2service.addRquiredException (user2); }Ejecutar métodos de verificación por separado y los resultados:
Análisis de los resultados de la base de datos de número de serie del método de verificación
| Número de serie del método de verificación | Resultados de la base de datos | Análisis de resultados |
|---|---|---|
| 1 | "Zhang San" y "Li Si" están insertados. | El método periférico no ha comenzado la transacción, y la inserción de los métodos "Zhang San" y "Li Si" se ejecutan independientemente en sus propias transacciones. El método periférico anormal no afecta la inserción interna de los métodos "Zhang San" y "Li Si". |
| 2 | Se inserta "Zhang San", pero no se inserta "Li Si". | El método periférico no tiene transacciones, y los métodos para insertar "Zhang San" y "Li Si" se ejecutan de forma independiente en sus propias transacciones, por lo que insertar el método "Li Si" solo retrocedirá el método "Li Si" e insertar el método "Zhang San" no se verá afectado. |
Conclusión: a través de estos dos métodos, demostramos que el método interno modificado por propagación. El requerido abrirá recientemente sus propias transacciones cuando el método periférico no abre la transacción, y las transacciones abiertas son independientes entre sí y no interferirán entre sí.
1.2 Escena 2
El método periférico inicia la transacción, que es un escenario con una tasa de uso relativamente alta.
Método de verificación 1:
@Override @Transactional (propagation = propagation.required) public void transaccion_exception_required_required () {user1 user1 = new User1 (); user1.setName ("Zhang San"); user1service.addrequired (user1); User2 user2 = new User2 (); user2.setName ("li si"); user2service.addRecued (user2); tirar nueva runtimeException (); }Método de verificación 2:
@Override @Transactional (propagation = propagation.required) public void transaccion_required_required_exception () {user1 user1 = new User1 (); user1.setName ("Zhang San"); user1service.addrequired (user1); User2 user2 = new User2 (); user2.setName ("li si"); user2service.addRquiredException (user2); }Método de verificación 3:
@Transactional @Override public void Transaction_required_required_exception_try () {user1 user1 = new User1 (); user1.setName ("Zhang San"); user1service.addrequired (user1); User2 user2 = new User2 (); user2.setName ("li si"); intente {user2service.addRquiredException (user2); } catch (Exception e) {System.out.println ("Method Rollback"); }}Ejecutar métodos de verificación por separado y los resultados:
| Número de serie del método de verificación | Resultados de la base de datos | Análisis de resultados |
|---|---|---|
| 1 | "Zhang San" y "Li Si" no fueron insertados. | El método periférico inicia la transacción, el método interno se une a la transacción del método periférico, el método periférico retrocede y el método interno también debe ser enrollado hacia atrás. |
| 2 | "Zhang San" y "Li Si" no fueron insertados. | El método periférico abre la transacción, el método interno agrega la transacción del método periférico, el método interno arroja una reversión de excepción y el método periférico percibe la excepción que hace que la transacción general se vuelva a revertir. |
| 3 | "Zhang San" y "Li Si" no fueron insertados. | El método periférico abre la transacción, el método interno se une a la transacción del método periférico y el método interno arroja una reversión de excepción. Incluso si el método es atrapado y no es percibido por el método periférico, toda la transacción aún se retira. |
Conclusión: Los resultados experimentales anteriores muestran que cuando el método periférico abre la transacción, los métodos internos modificados por Propagation.REQUIRED Se agregará a la transacción del método periférico. Todos los métodos internos y métodos periféricos modificados por Propagation.REQUIRED Se requirieron la misma transacción. Mientras un método regrese, toda la transacción se volverá hacia atrás.
2.propagation_requires_new
Agregamos el atributo Propagation.REQUIRES_NEW a los métodos correspondientes de user1service y user2service.
Método de servicio de usuario1:
@ServicePublic Class User1ServiceImpl implementa User1Service {// omitir otros ... @Override @TransactionAL (propagation = propagation.requires_new) public void addRequiresnew (user1 user) {user1mapper.insert (user); } @Override @transactional (propagation = propagation.required) public void addRecued (user1 user) {user1mapper.insert (usuario); }}Método user2Service:
@ServicePublic Class User2ServiceImpl implementa User2Service {// omitir otros ... @Override @TransactionAL (propagation = propagation.requires_new) public void addRequiresnew (user2 user) {user2mapper.insert (user); } @Override @transactional (propagation = propagation.requires_new) public void addRequiresNewException (user2 user) {user2mapper.insert (user); tirar nueva runtimeException (); }} 2.1 Escena 1
El método periférico no habilita las transacciones.
Método de verificación 1:
@Override public void Nottransaction_exception_requiresnew_requiresnew () {user1 user1 = new User1 (); user1.setName ("Zhang San"); user1service.AddRequiresNew (User1); User2 user2 = new User2 (); user2.setName ("li si"); user2service.addrequiresnew (user2); tirar nueva runtimeException (); }Método de verificación 2:
@Override public void NotTransaction_requiresnew_requiresnew_exception () {user1 user1 = new User1 (); user1.setName ("Zhang San"); user1service.AddRequiresNew (User1); User2 user2 = new User2 (); user2.setName ("li si"); user2service.AddRecureSnewException (user2); }Ejecutar métodos de verificación por separado y los resultados:
| Número de serie del método de verificación | Resultados de la base de datos | Análisis de resultados |
|---|---|---|
| 1 | Se inserta "Zhang San" y se inserta "Li Si". | El método periférico no tiene transacciones. La inserción de los métodos "Zhang San" y "Li Si" se ejecutan de forma independiente en sus propias transacciones. La reversión de la excepción del método periférico no afectará el método interno. |
| 2 | Se inserta "Zhang San", "Li Si" no está insertado | El método periférico no inicia la transacción. La inserción del método "Zhang San" y la inserción del método "Li Si" comienzan sus propias transacciones respectivamente. La inserción del método "Li Si" arroja una reversión de excepción, y otras transacciones no se ven afectadas. |
Conclusión: a través de estos dos métodos, demostramos que el método interno modificado por Propagation.REQUIRES_NEW
2.2 Escena 2
El método periférico inicia la transacción.
Método de verificación 1:
@Override @Transactional (propagation = propagation.required) public void transaccion_exception_required_requiresnew_requiresnew () {user1 user1 = new user1 (); user1.setName ("Zhang San"); user1service.addrequired (user1); User2 user2 = new User2 (); user2.setName ("li si"); user2service.addrequiresnew (user2); User2 user3 = new User2 (); user3.setName ("Wang Wu"); user2service.AdDRecureiresnew (User3); tirar nueva runtimeException (); }Método de verificación 2:
@Override @Transactional (propagation = propagation.required) public void transaccion_required_requiresnew_requiresnew_exception () {user1 user1 = new user1 (); user1.setName ("Zhang San"); user1service.addrequired (user1); User2 user2 = new User2 (); user2.setName ("li si"); user2service.addrequiresnew (user2); User2 user3 = new User2 (); user3.setName ("Wang Wu"); user2service.addrequiresnewexception (user3); }Método de verificación 3:
@Override @Transactional (propagation = propagation.required) public void transaccion_required_requiresnew_requiresnew_exception_try () {user1 user1 = new user1 (); user1.setName ("Zhang San"); user1service.addrequired (user1); User2 user2 = new User2 (); user2.setName ("li si"); user2service.addrequiresnew (user2); User2 user3 = new User2 (); user3.setName ("Wang Wu"); Pruebe {user2service.AddRequiresNewException (user3); } capt (excepción e) {system.out.println ("rollingback"); }}Ejecutar métodos de verificación por separado y los resultados:
| Número de serie del método de verificación | Resultados de la base de datos | Análisis de resultados |
|---|---|---|
| 1 | No se insertó "Zhang San", se insertó "Li Si" y se insertó "Wang Wu". | El método periférico inicia la transacción, inserta una transacción del método "Zhang San" y el método periférico, inserta el método "Li Si" y el método "Wang Wu" respectivamente en la transacción recién creada independiente. El método periférico arroja una excepción y solo retrocede la misma transacción que el método periférico, por lo que el método de insertar el método "Zhang San" vuelve a revertir. |
| 2 | No se insertó "Zhang San", "Li Si" se insertó y no se insertó "Wang Wu". | El método periférico inicia la transacción, inserta una transacción del método "Zhang San" y el método periférico, inserta el método "Li Si" y el método "Wang Wu" en nuevas transacciones independientes. Cuando se inserta el método "Wang Wu", la transacción que se inserta en el método "Wang Wu" se enrolla hacia atrás. La excepción continúa siendo lanzada y es percibida por el método periférico. La transacción del método periférico también se remonta, por lo que el método "Zhang San" también se retira hacia atrás. |
| 3 | Se inserta "Zhang San", se inserta "Li Si" y no se inserta "Wang Wu". | El método periférico inicia la transacción, inserta una transacción del método "Zhang San" y el método periférico, inserta el método "Li Si" y el método "Wang Wu" en nuevas transacciones independientes. Se inserta el método "Wang Wu" y la transacción que inserta el método "Wang Wu" se retira hacia atrás. La excepción es atrapada y no será percibida por el método periférico. La transacción del método periférico no se retrocede, por lo que la inserción del método "Zhang San" se inserta correctamente. |
Conclusión: cuando el método periférico abre la transacción, el método interno modificado por Propagation.REQUIRES_NEW . El método interno, el método interno y las transacciones del método externo son independientes entre sí y no interfieren entre sí.
3.propagation_nested
Agregamos Propagation.NESTED Atributos inestables a los métodos correspondientes de user1service y user2service.
Método de servicio de usuario1:
@ServicePublic Class User1ServiceImpl implementa User1Service {// omitir a otros ... @Override @Transactional (propagation = propagation.nested) public void addnested (user1 user) {user1mapper.insert (user); }}Método user2Service:
@ServicePublic Class User2ServiceImpl implementa User2Service {// omitir otros ... @Override @TransactionAL (propagation = propagation.nested) public void addnested (user2 user) {user2mapper.insert (user); } @Override @transactional (propagation = propagation.nested) public void addendestException (user2 user) {user2mapper.insert (usuario); tirar nueva runtimeException (); }} 3.1 Escena 1
Este método periférico de escenario no habilita las transacciones.
Método de verificación 1:
@Override public void Nottransaction_exception_nested_nested () {user1 user1 = new User1 (); user1.setName ("Zhang San"); user1service.adnested (user1); User2 user2 = new User2 (); user2.setName ("li si"); user2service.adnested (user2); tirar nueva runtimeException (); }Método de verificación 2:
@Override public void Nottransaction_nested_nested_exception () {user1 user1 = new User1 (); user1.setName ("Zhang San"); user1service.adnested (user1); User2 user2 = new User2 (); user2.setName ("li si"); user2service.AdDnestedException (user2); }Ejecutar métodos de verificación por separado y los resultados:
| Número de serie del método de verificación | Resultados de la base de datos | Análisis de resultados |
|---|---|---|
| 1 | "Zhang San" y "Li Si" están insertados. | El método periférico no ha comenzado la transacción, y la inserción de los métodos "Zhang San" y "Li Si" se ejecutan independientemente en sus propias transacciones. El método periférico anormal no afecta la inserción interna de los métodos "Zhang San" y "Li Si". |
| 2 | Se inserta "Zhang San", pero no se inserta "Li Si". | El método periférico no tiene transacciones, y los métodos para insertar "Zhang San" y "Li Si" se ejecutan de forma independiente en sus propias transacciones, por lo que insertar el método "Li Si" solo retrocedirá el método "Li Si" e insertar el método "Zhang San" no se verá afectado. |
Conclusión: a través de estos dos métodos, demostramos que Propagation.REQUIRED Propagation.NESTED . Los métodos internos modificados comenzarán sus propias transacciones nuevamente, y las transacciones abiertas son independientes entre sí y no interfieren entre sí.
3.2 Escena 2
El método periférico inicia la transacción.
Método de verificación 1:
@Transactional @Override public Void Transaction_Exception_Nested_Nested () {user1 user1 = new User1 (); user1.setName ("Zhang San"); user1service.adnested (user1); User2 user2 = new User2 (); user2.setName ("li si"); user2service.adnested (user2); tirar nueva runtimeException (); }Método de verificación 2:
@Transactional @Override public void Transaction_nested_nested_exception () {user1 user1 = new User1 (); user1.setName ("Zhang San"); user1service.adnested (user1); User2 user2 = new User2 (); user2.setName ("li si"); user2service.AdDnestedException (user2); }Método de verificación 3:
@Transactional @Override public Void Transaction_nested_nested_exception_try () {user1 user1 = new User1 (); user1.setName ("Zhang San"); user1service.adnested (user1); User2 user2 = new User2 (); user2.setName ("li si"); intente {user2service.AdDnestedException (user2); } catch (Exception e) {System.out.println ("Method Rollback"); }}Ejecutar métodos de verificación por separado y los resultados:
| Número de serie del método de verificación | Resultados de la base de datos | Análisis de resultados |
|---|---|---|
| 1 | "Zhang San" y "Li Si" no fueron insertados. | El método periférico inicia la transacción, y la transacción interna es una sub-transacción de la transacción periférica. El método periférico regresa, y el método interno también debe ser retrocedido. |
| 2 | "Zhang San" y "Li Si" no fueron insertados. | El método periférico inicia la transacción, y la transacción interna es una sub-transacción de la transacción periférica. El método interno arroja una reversión de excepción, y el método periférico percibe la excepción que hace que la transacción general vuelva a revertir. |
| 3 | Se inserta "Zhang San" y no se inserta "Li Si". | El método periférico inicia la transacción, y la transacción interna es una sub-transacción de la transacción periférica. Inserte el método interno "Zhang San" para lanzar una excepción, y la transacción infantil se puede enrollar por separado. |
Conclusión: Los resultados de la prueba anterior muestran que cuando el método periférico abre la transacción, el método interno modificado por Propagation.NESTED . La transacción principal periférica regresa, y la sub-transacción debe retroceder. La sub-transacción interna se puede volver por separado sin afectar la transacción principal periférica y otras subransacciones.
4. Similitudes y similitudes de requerido, requerido_new, anidado
A partir de la comparación de "1.2 escena 2" y "3.2 escena 2", podemos ver:
Los métodos internos modificados por anidados y requeridos son ambas transacciones de métodos periféricos. Si el método periférico arroja una excepción, las transacciones de ambos métodos se volverán hacia atrás. Sin embargo, requerido se une a las transacciones del método periférico, por lo que pertenece a la misma transacción que las transacciones periféricas. Una vez que la transacción requerida arroja una excepción y se enrolla hacia atrás, las transacciones del método periférico también se enrollarán hacia atrás. Anidados es una sub-transacción del método periférico y tiene un punto de guardado separado, por lo que el método anidado arroja una excepción y se retira hacia atrás, lo que no afectará la transacción del método periférico.
A partir de la comparación de "2.2 escena 2" y "3.2 escena 2", podemos ver:
Ambos anidados y requeridos_new pueden retroceder las transacciones del método interno sin afectar las transacciones del método periférico. Sin embargo, debido a que anidada es una transacción anidada, después de que el método periférico se retire hacia atrás, las sub-transacciones que son transacciones del método periférico también se retirarán hacia atrás. Requies_new se implementa abriendo una nueva transacción. Las transacciones internas y las transacciones periféricas son dos transacciones. La reversión de la transacción periférica no afectará las transacciones internas.
5. Otros comportamientos de propagación de transacciones
En vista del problema de la longitud del artículo, las pruebas de otros comportamientos de propagación de transacciones no se describirán aquí. Los lectores interesados pueden buscar el código de prueba correspondiente y las explicaciones de los resultados en el código fuente. Portal: https: //github.com/tmtse/tran ...
Casos de uso de simulación
Después de introducir tantos comportamientos de comunicación de transacciones, ¿cómo los aplicamos en nuestro trabajo real? Déjame darte un ejemplo:
Supongamos que tenemos un método registrado, en el que se llama el método de agregar puntos. Si queremos agregar puntos para no afectar el proceso de registro (es decir, la reversión no pudo agregar puntos no puede hacer que el método de registro sea retroceder también), escribiremos esto:
@Service Public Class UserServiceImpl implementa UserService {@Transactional public void Register (usuario de usuario) {try {memberShipPointService.addpoint (punto de punto); } catch (excepción e) {// omitir ...} // omitir ...} // omitir ...} También estipulamos que la falla de registro afectará addPoint() (el retroceso del método de registro también requiere reversión), por lo que addPoint() debe implementarse así:
@Service public class MembersHipPointServiceImpl implementa membershipPointService {@transactional (propagation = propagation.nested) public void addPoint (punto de punto) {try {regesservice.addrecord (registro registro); } catch (excepción e) {// omitir ...} // omitir ...} // omitir ...} Notamos que addRecord() addPoint() , que se utiliza para grabar registros. Su implementación es la siguiente:
@Service public class RecordServiceImpl implementa Registros de registros {@transactional (propagation = propagation.not_supported) public void addRecord (registro registro) {// omit ...} // omit ...} Notamos propagation = Propagation.NOT_SUPPORTED en addRecord() , porque no es preciso para el registro, y uno puede ser más o menos, por lo que addRecord() en sí mismo y el método periférico addPoint() no causará addRecord() para hacer retroceder, y addRecord() para lanzar una excepción no la excepción no hará que la ejecución de la ejecución de la ejecución de la ejecución de la ejecución de la ejecución de la ejecución de la ejecución de la ejecución de la ejecución de la ejecución de la ejecución de la ejecución de la ejecución de la ejecución de la ejecución de la addPoint() de la excepción.
A través de este ejemplo, creo que todos tienen una comprensión más intuitiva del uso del comportamiento de comunicación de transacciones. La combinación de varios atributos puede hacer que nuestra implementación comercial sea más flexible y diversa.
en conclusión
A través de la introducción anterior, creo que todos tienen una comprensión más profunda del comportamiento de comunicación de transacciones de primavera, y espero que su trabajo de desarrollo diario sea útil.
Resumir
Lo anterior es todo el contenido de este artículo. Espero que el contenido de este artículo tenga cierto valor de referencia para el estudio o el trabajo de todos. Si tiene alguna pregunta, puede dejar un mensaje para comunicarse. Gracias por su apoyo a Wulin.com.