Gestión de transacciones programadas
Una persona tan excelente no debe dejar que pierda ni un minuto, así que pronto le envié el documento de requisitos y el código fuente para que se familiarizara con el proceso de negocio y desarrollo localmente.
Inesperadamente, descubrimos un problema cuando estábamos revisando el código el miércoles. El nuevo colega optimizó directamente el @Transactional original en este fantasma:
Debido a esta línea de código, el jefe (que también era un buen jugador en un fabricante de Internet de primer nivel en ese entonces) se enojó con el Estaba a punto de persuadir al nuevo colega, rápidamente tomé una decisión. Al fin y al cabo, la persona a la que estás entrevistando no mira el rostro de un monje sino el de un Buda, ¿verdad? Entonces mi jefe me prometió intentarlo un mes más.
Después de la reunión, rápidamente pedí a mis nuevos colegas que revisaran los asuntos. El siguiente es su propio resumen, que aún es muy detallado. Déjame compartirlo para tu referencia e inspiración. Creo que después de leer esto comprenderá por qué la anotación @Transactional no se puede optimizar de esta manera. Es puramente superfluo e indiscriminado.
Una transacción es lógicamente un conjunto de operaciones que se ejecutan o no se ejecutan. Principalmente para bases de datos, como MySQL.
Siempre que recuerde esto, será fácil entender el trading. En Java, normalmente tenemos que manejar múltiples eventos en nuestro negocio. Por ejemplo, Programming Meow tiene un método para guardar artículos. No solo guarda el artículo en sí, sino que también guarda las etiquetas correspondientes al artículo. Las etiquetas y los artículos no están en la misma tabla, pero la tabla de etiquetas (tag) está relacionada guardando la clave principal de la etiqueta (tag_id) en la tabla de artículos (article):
En este momento, debe Inicie una transacción para garantizar que el artículo Los datos de la tabla y la tabla de etiquetas estén sincronizados, o ambos se ejecutan o ninguno se ejecuta.
De lo contrario, es posible que el artículo se guarde correctamente, pero las etiquetas pueden fallar, o es posible que el artículo no se guarde, pero las etiquetas se pueden guardar correctamente; estos escenarios no están en línea con nuestras expectativas.
Para garantizar la exactitud y confiabilidad de las transacciones, se deben mostrar cuatro características importantes de ACID al escribir o actualizar la base de datos:
Entre ellas, el aislamiento de transacciones se divide en cuatro diferentes niveles, que incluyen:
Cabe señalar que si una transacción puede tener efecto depende de si el motor de la base de datos admite transacciones. El motor InnoDB de MySQL admite transacciones, pero MyISAM no.
1) Transacción programada
La transacción programada se refiere a incorporar código de administración de transacciones en el código comercial para controlar el envío y la reversión de transacciones.
Por ejemplo, usa TransactionTemplate para administrar transacciones:
Otro ejemplo, usa TransactionManager para administrar transacciones:
En términos de administración de transacciones programáticas, Spring es más Como usar TransactionTemplate.
En las transacciones programáticas, cada operación comercial debe contener código de administración de transacciones adicional, lo que hace que el código parezca muy inflado, pero es muy útil para comprender el modelo de administración de transacciones de Spring.
Por supuesto, para lograr la separación de la gestión de transacciones y el código comercial, se debe utilizar una de las tecnologías más críticas y centrales de Spring, AOP. Su esencia es interceptar los métodos antes y después, luego crear o unirse a una transacción antes de que comience el método de destino y confirmar o revertir de acuerdo con el estado de ejecución después de que se ejecute el método de destino.
Spring resume el núcleo de la gestión de transacciones en TransactionManager. Su código fuente solo tiene una definición de interfaz simple, que pertenece a una interfaz de marca:
A través de la interfaz PlatformTransactionManager, Spring proporciona varias plataformas. con administradores de transacciones correspondientes, como JDBC (DatasourceTransactionManager), Hibernate (administrador de transacciones de Hibernate), JPA (JpaTransactionManager), etc. , pero la implementación específica es cuestión de cada plataforma.
El parámetro TransactionDefinition corresponde a la anotación @Transactional.
Por ejemplo, las propiedades definidas en la anotación @Transactional, como el comportamiento de propagación de la transacción, el nivel de aislamiento, el tiempo de espera de la transacción y si la transacción es de solo lectura, se pueden encontrar en TransactionDefinition.
El tipo de devolución TransactionStatus se utiliza principalmente para almacenar algunos estados y datos de la transacción actual, como el estado de conexión y reversión.
TransactionDefinition.java:
Transactional.java
Dicho esto, expliquemos en detalle el comportamiento de propagación, el nivel de aislamiento, el período de tiempo de espera y las propiedades de lectura y reglas de reversión.
Cuando un método de transacción es llamado por otro método de transacción, es necesario especificar cómo se debe propagar la transacción. Por ejemplo, el método podría continuar la ejecución dentro de la transacción actual o podría abrir una nueva transacción para ejecutarla dentro de su propia transacción.
TransactionDefinition-* * define siete tipos de comportamientos de propagación de transacciones:
01. Propagation_required
Este es también el comportamiento de propagación de transacciones predeterminado de @Transactional, es decir. Dice que si actualmente existe una transacción, se agregará; si no hay ninguna transacción actualmente, se creará una nueva transacción. Más precisamente, significa:
Este comportamiento de comunicación también es el más fácil de entender. aMethod llama a bMethod y, siempre que uno de los métodos se revierta, se revertirá toda la transacción.
02. Propagate_request_new
Crear una nueva transacción y suspender la transacción actual (si corresponde). Es decir, independientemente de si el método externo abre una transacción, el método interno modificado se propaga. REQUIRES_NEW _ New abrirá su propia transacción. La transacción abierta y la transacción externa son independientes y no interfieren entre sí.
Si aMethod() se revierte de forma anormal, bMethod() no se revertirá porque bMethod() inicia una transacción independiente. Sin embargo, si bMethod() genera una excepción no detectada y esta excepción cumple con las reglas de reversión de transacciones, entonces aMethod() también se revertirá.
03. Propagation_nesting
Si actualmente hay una transacción, se ejecutará dentro de la transacción actual; de lo contrario, se ejecutará una operación similar a PROPAGATION_REQUIRED.
04. Propagation_Force
Si actualmente hay una transacción, se agregará la transacción; si no hay una transacción actual, se lanzará una excepción.
05. Propagation_Support
Si actualmente hay una transacción, la transacción se agregará; si actualmente no hay ninguna transacción, continuará ejecutándose de manera no transaccional.
06. No se admite la propagación
Ejecutar en modo no transaccional Si hay una transacción, la transacción actual se suspenderá.
07. Propagation_Never
Ejecutar en modo no transaccional Si actualmente hay una transacción, se lanzará una excepción.
3, 4, 5, 6 y 7 no se usan comúnmente, solo conócelos.
Ya hemos entendido el nivel de aislamiento de transacciones de la base de datos, y es mucho más fácil entender el nivel de aislamiento de transacciones de Spring.
* * * en TransactionDefinition define cinco niveles de aislamiento de transacciones:
Por lo general, solo usamos el nivel de aislamiento predeterminado ISOLATION_DEFAULT, lo que significa dejar que la base de datos decida, podemos elegir @ El El comando @transaction_aislamiento verifica el nivel de aislamiento predeterminado de MySql. El resultado es REPEATABLE-READ, que significa lectura repetible.
Tiempo de espera de la transacción, que es el tiempo máximo que se permite ejecutar una transacción. Si no se completa dentro del período de tiempo de espera, se revertirá automáticamente.
Si el tiempo de ejecución de la transacción es particularmente largo, debido a que la transacción implica bloquear la base de datos, la transacción de larga duración ocupará recursos de la base de datos.
Si una transacción solo lee la base de datos, la base de datos puede aprovechar la naturaleza de solo lectura de la transacción y tomar medidas de optimización, lo cual es adecuado para múltiples operaciones de consulta de bases de datos.
Esto se debe a que MySql (innodb) habilita el modo de confirmación automática para cada conexión de forma predeterminada.
En este modo, cada declaración SQL enviada al servidor MySql se procesará en una transacción separada y la transacción se confirmará automáticamente después de la ejecución.
Entonces, si anotamos este método con @Transactional, todo el SQL de este método se colocará en una transacción. De lo contrario, cada SQL abrirá una transacción separada y los datos modificados por otras transacciones se leerán en tiempo real.
En algunos casos, cuando se ejecutan varias declaraciones de consulta simultáneamente y es necesario garantizar la coherencia de los datos, es necesario habilitar el soporte de transacciones. De lo contrario, si otros usuarios cambian los datos después de la última consulta SQL, la siguiente consulta SQL puede ser inconsistente.
De forma predeterminada, las transacciones solo se revierten cuando ocurre una excepción en tiempo de ejecución, y cuando ocurre una excepción marcada (que debe detectarse o lanzarse activamente), el error no se revierte.
Si desea revertir un tipo de excepción específico, puede configurarlo de esta manera:
En el pasado, necesitábamos configurar Spring para administrar transacciones a través de XML. Con Spring Boot, todo se vuelve simple. Solo necesitamos agregar comentarios de transacción (@Transactional) en la capa empresarial para abrir rápidamente la transacción.
En otras palabras, solo debemos prestar atención a la anotación @Transactional.
Aunque hay muchas propiedades definidas en el código fuente anotado @Transactional, la mayoría de las veces uso la configuración predeterminada. Por supuesto, si se requiere personalización, se ha explicado anteriormente.
1) Para ser utilizado en el método público, hay un juicio en el método ComputeTransactionAttribute de la clase abstractfallbackTransactionAttributeSource. Si el método de destino no es público, TransactionAttribute devuelve nulo, es decir, la transacción no es compatible.
2) Evite llamar a métodos anotados @Transactional en la misma clase, ya que esto hará que la transacción falle.
Antes de realizar la prueba, primero ajustamos la información del nivel de registro predeterminado de Spring Boot para depuración y modificación en el archivo application.yml:
Luego, mira los datos encontrados antes de la modificación:
Empecemos. Agregue una interfaz de actualización al controlador, prepárese para modificar los datos y planee modificar el dogleg de Silent King 2 al dogleg de Silent King 2:
Anote este método con @Transactional en el servicio y desencadenar una excepción en tiempo de ejecución:
Según nuestras expectativas, después de guardar los datos, la transacción se revertirá debido a una excepción. Por lo que los datos no serán modificados.
Ingrese http://localhost:8080/user/update en el navegador para realizar la prueba y preste atención al registro para confirmar que la transacción ha tenido efecto.
Cuando eliminamos la transacción, también arrojaremos una excepción:
Cuando se ejecuta nuevamente, encontramos que aunque el programa informó un error, los datos aún estaban actualizados.
Esto también prueba indirectamente que nuestra transacción @Transactional funciona.
Al ver esto, entiendes por qué la optimización de nuevos compañeros es puramente redundante, ¿verdad?