El capítulo cuarenta y cinco de Spring's Road to God: lo llevará a comprender los 7 tipos de comportamientos de comunicación de los asuntos de Spring

Este artículo explica en detalle los siete comportamientos de propagación en las transacciones de Spring, lo cual es bastante importante.

ambiente

  1. jdk1.8
  2. Primavera 5.2.3.LIBERAR
  3. mysql5.7

¿Qué es el comportamiento de propagación de transacciones?

El comportamiento de propagación de la transacción se usa para describir: algunos métodos en el sistema se entregan a Spring para administrar la transacción. Cuando hay llamadas anidadas entre estos métodos, ¿cuál es el comportamiento de la transacción?

Por ejemplo, en las siguientes dos clases, el método m1 en Service1 y el método m2 en Service2 tienen anotaciones @Transactional, lo que indica que estos dos métodos están controlados por Spring.

Pero preste atención a las 2 líneas de código en m1, primero ejecute una inserción, luego llame al método m2 en service2, y el método m2 en service2 también ejecuta una inserción.

Entonces, ¿crees que estas dos inserciones se ejecutarán en una transacción? En otras palabras, ¿cuál es el comportamiento de la transacción en este momento? Esto es lo que está controlado por el comportamiento de propagación de las transacciones de primavera. Los diferentes comportamientos de propagación se comportarán de manera diferente. Pueden o no ejecutarse en una transacción, lo que depende de la configuración del comportamiento de propagación. .

@Component
public class Service1 {
    
    
    @Autowired
    private Service2 service2;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Transactional
    public void m1() {
    
    
        this.jdbcTemplate.update("INSERT into t1 values ('m1')");
        this.service2.m2();
    }
}

@Component
public class Service2 {
    
    
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Transactional
    public void m2() {
    
    
        this.jdbcTemplate.update("INSERT into t1 values ('m2')");
    }
}

¿Cómo configurar el comportamiento de propagación de transacciones?

Especifique el comportamiento de propagación de la transacción a través del atributo de propagación en la anotación @Transactional :

Propagation propagation() default Propagation.REQUIRED;

La propagación es una enumeración con 7 valores, como sigue:

Tipo de comportamiento de propagación de transacciones ilustrar
REQUERIDO Si no hay una transacción en el administrador de transacciones actual, cree una nueva transacción, si ya hay una transacción, únase a esta transacción. Esta es la opción más común y es el comportamiento de propagación predeterminado .
SOPORTES Admite la transacción actual, si no hay ninguna transacción en el administrador de transacciones actual, se ejecutará de forma no transaccional.
OBLIGATORIO Use la transacción actual o lance una excepción si no hay ninguna transacción en el administrador de transacciones actual.
REQUIERE_NUEVO Cree una nueva transacción, si hay una transacción en el administrador de transacciones actual, suspenda la transacción actual y luego cree una nueva transacción.
NO SOPORTADO Ejecutar la operación de manera no transaccional. Si hay una transacción en el administrador de transacciones actual, suspender la transacción actual.
NUNCA Se ejecuta de forma no transaccional, lanzando una excepción si existe una transacción en el administrador de transacciones actual.
ANIDADO Si hay una transacción en el administrador de transacciones actual, ejecútela dentro de la transacción anidada; si no hay ninguna transacción en el administrador de transacciones actual, realice una operación similar a PROPAGATION_REQUIRED.

Nota: Hay una premisa para estos 7 tipos de comportamientos de propagación: cuando sus administradores de transacciones son los mismos, tendrán el comportamiento de rendimiento descrito anteriormente.

La siguiente es una ilustración del comportamiento de desempeño en un caso 7. Antes de ver el caso, repasemos algunos puntos de conocimiento

1. El proceso de transacción de procesamiento de transacciones declarativas de Spring

La transacción declarativa Spring implementa la función de gestión de transacciones al interceptar el método de destino a través del interceptor de transacciones TransactionInterceptor.El proceso de procesamiento del administrador de transacciones es más o menos el siguiente:

1、获取事务管理器
2、通过事务管理器开启事务
try{
 3、调用业务方法执行db操作
 4、提交事务
}catch(RuntimeException | Error){
 5、回滚事务
}

2. ¿Cuándo se revertirá la transacción?

De forma predeterminada, cuando el método de destino arroja una RuntimeException o un Error , la transacción se revertirá .

3. ¿Cómo usar la misma conexión en el administrador de transacciones de Spring y la conexión para operar la base de datos en el negocio?

Tome DataSourceTransactionManager como administrador de transacciones y use JdbcTemplate para operar db para explicar.

Al crear DataSourceTransactionManager y JdbcTemplate, debe especificar dataSource y debe especificar su dataSource como el mismo objeto.

Cuando el administrador de transacciones inicia una transacción, obtendrá una conexión de conexión db a través del método dataSource.getConnection(), luego lanzará la conexión dataSource-> en un mapa y luego colocará el mapa en ThreadLocal.

Cuando JdbcTemplate ejecuta sql, use JdbcTemplate.dataSource para buscar en ThreadLocal anterior para ver si hay una conexión disponible, si es así, úsela directamente, de lo contrario, llame al método JdbcTemplate.dataSource.getConnection() para obtener una conexión para usar.

Por lo tanto, en Spring, puede asegurarse de que la conexión en el administrador de transacciones y la conexión que opera la base de datos en JdbcTemplate sean las mismas, para garantizar que Spring pueda controlar la transacción.

verificación de código

preparar base de datos

DROP DATABASE IF EXISTS javacode2018;
CREATE DATABASE if NOT EXISTS javacode2018;

USE javacode2018;
DROP TABLE IF EXISTS user1;
CREATE TABLE user1(
  id int PRIMARY KEY AUTO_INCREMENT,
  name varchar(64) NOT NULL DEFAULT '' COMMENT '姓名'
);

DROP TABLE IF EXISTS user2;
CREATE TABLE user2(
  id int PRIMARY KEY AUTO_INCREMENT,
  name varchar(64) NOT NULL DEFAULT '' COMMENT '姓名'
);

Clase de configuración Spring MainConfig6

Prepare JdbcTemplate y el administrador de transacciones.

package com.javacode2018.tx.demo6;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

@EnableTransactionManagement //开启spring事务管理功能
@Configuration //指定当前类是一个spring配置类
@ComponentScan //开启bean扫描注册
public class MainConfig6 {
    
    
    //定义一个数据源
    @Bean
    public DataSource dataSource() {
    
    
        org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8");
        dataSource.setUsername("root");
        dataSource.setPassword("root123");
        dataSource.setInitialSize(5);
        return dataSource;
    }

    //定义一个JdbcTemplate,用来执行db操作
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
    
    
        return new JdbcTemplate(dataSource);
    }

    //定义我一个事务管理器
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
    
    
        return new DataSourceTransactionManager(dataSource);
    }
}

Ven a 3 servicios

En los siguientes casos, se utilizarán transacciones de primavera en estos tres servicios para demostrar el efecto.

Usuario1Servicio

package com.javacode2018.tx.demo6;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

@Component
public class User1Service {
    
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
}

Usuario2Servicio

package com.javacode2018.tx.demo6;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

@Component
public class User2Service {
    
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
}

Servicio Tx

package com.javacode2018.tx.demo6;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class TxService {
    
    
    @Autowired
    private User1Service user1Service;
    @Autowired
    private User2Service user2Service;
}

Caso de prueba Demo6Test

El método before se ejecutará una vez antes de cada método marcado con @Test. Este método se utiliza principalmente para realizar algunos trabajos de preparación: iniciar el contenedor de primavera y limpiar los datos en las dos tablas; el método after se ejecutará después de cada método marcado con @Test. Después de ejecutarlo una vez, sacamos los datos de 2 tablas en este; es conveniente ver el efecto del caso de prueba.

package com.javacode2018.tx.demo6;

import org.junit.Before;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Demo6Test {
    
    

    private TxService txService;
    private JdbcTemplate jdbcTemplate;

    //每个@Test用例执行之前先启动一下spring容器,并清理一下user1、user2中的数据
    @Before
    public void before() {
    
    
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig6.class);
        txService = context.getBean(TxService.class);
        jdbcTemplate = context.getBean(JdbcTemplate.class);
        jdbcTemplate.update("truncate table user1");
        jdbcTemplate.update("truncate table user2");
    }

    @After
    public void after() {
    
    
        System.out.println("user1表数据:" + jdbcTemplate.queryForList("SELECT * from user1"));
        System.out.println("user2表数据:" + jdbcTemplate.queryForList("SELECT * from user2"));
    }

}

1, REQUERIDO

Usuario1Servicio

Agregar 1 método, comportamiento de propagación de transacciones: REQUERIDO

@Transactional(propagation = Propagation.REQUIRED)
public void required(String name) {
    
    
    this.jdbcTemplate.update("insert into user1(name) VALUES (?)", name);
}

Usuario2Servicio

Agregue 2 métodos, comportamiento de propagación de transacciones: REQUERIDO, tenga en cuenta que la última línea dentro del segundo método generará una excepción.

@Transactional(propagation = Propagation.REQUIRED)
public void required(String name) {
    
    
    this.jdbcTemplate.update("insert into user1(name) VALUES (?)", name);
}

@Transactional(propagation = Propagation.REQUIRED)
public void required_exception(String name) {
    
    
    this.jdbcTemplate.update("insert into user1(name) VALUES (?)", name);
    throw new RuntimeException();
}

Escena 1 (1-1)

El método periférico no tiene transacción , y el método periférico llama internamente a dos métodos de transacción de nivel REQUERIDO.

En este caso, los otros dos servicios se llaman en el método de TxService, por lo que los métodos en TxService se denominan colectivamente métodos periféricos y los métodos en los otros dos servicios se denominan métodos internos.

Método de verificación 1

Agregar Servicio Tx
public void notransaction_exception_required_required() {
    
    
    this.user1Service.required("张三");
    this.user2Service.required("李四");
    throw new RuntimeException();
}
Caso de prueba, agregado en Demo6Test
@Test
public void notransaction_exception_required_required() {
    
    
    txService.notransaction_exception_required_required();
}
ejecutar salida
user1表数据:[{id=1, name=张三}]
user2表数据:[{id=1, name=李四}]

Método de verificación 2

Agregar Servicio Tx
public void notransaction_required_required_exception() {
    
    
    this.user1Service.required("张三");
    this.user2Service.required_exception("李四");
}
Caso de prueba, agregado en Demo6Test
@Test
public void notransaction_required_required_exception() {
    
    
    txService.notransaction_required_required_exception();
}
ejecutar salida
user1表数据:[{id=1, name=张三}]
user2表数据:[]

Análisis de resultados

Número de serie del método de verificación resultado de la base de datos Análisis de resultados
1 Se insertan tanto "Zhang San" como "Li Si". El método periférico no abre la transacción , y los métodos insertados "Zhang San" y "Li Si" se ejecutan de forma independiente en su propia transacción, y la anormalidad del método periférico no afecta las transacciones independientes de la inserción interna del "Zhang Métodos San" y "Li Si".
2 Se inserta "Zhang San", pero no se inserta "Li Si". El método periférico no tiene transacción , y los métodos de inserción de "Zhang San" y "Li Si" se ejecutan de forma independiente en sus propios asuntos, por lo que si el método de inserción de "Li Si" arroja una excepción, solo se revertirá. Al insertar el método "Li Si", al insertar el método "Zhang San" no se ve afectado.

en conclusión

Propagation.REQUIREDA través de estos dos métodos, demostramos que el método interno modificado abrirá su propia transacción cuando el método periférico no abra la transacción , y las transacciones abiertas son independientes entre sí y no interfieren entre sí.

Escena 2 (1-2)

El método periférico inicia la transacción (Propagation.REQUIRED), que se usa con mucha frecuencia.

Método de verificación 1

Agregar Servicio Tx
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_exception_required_required() {
    
    
    user1Service.required("张三");
    user2Service.required("李四");
    throw new RuntimeException();
}
Caso de prueba, agregado en Demo6Test
@Test
public void transaction_exception_required_required() {
    
    
    txService.transaction_exception_required_required();
}
ejecutar salida
user1表数据:[]
user2表数据:[]

Método de verificación 2

Agregar Servicio Tx
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_required_required_exception() {
    
    
    user1Service.required("张三");
    user2Service.required_exception("李四");
}
Caso de prueba, agregado en Demo6Test
@Test
public void transaction_required_required_exception() {
    
    
    txService.transaction_required_required_exception();
}
ejecutar salida
user1表数据:[]
user2表数据:[]

Método de verificación 3

Agregar Servicio Tx
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_required_required_exception_try() {
    
    
    user1Service.required("张三");
    try {
    
    
        user2Service.required_exception("李四");
    } catch (Exception e) {
    
    
        System.out.println("方法回滚");
    }
}
Caso de prueba, agregado en Demo6Test
@Test
public void transaction_required_required_exception_try() {
    
    
    txService.transaction_required_required_exception_try();
}
ejecutar salida
方法回滚
user1表数据:[]
user2表数据:[]

Análisis de resultados

Número de serie del método de verificación resultado de la base de datos Análisis de resultados
1 "Zhang San" y "Li Si" no están 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 retrocede .
2 "Zhang San" y "Li Si" no están 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 interno lanza una excepción y retrocede, y el método periférico percibe la excepción y hace que la transacción general retroceda .
3 "Zhang San" y "Li Si" no están 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 y el método interno lanza una excepción y retrocede Incluso si el método es capturado y no es percibido por el método periférico, toda la transacción aún se retrotrae .

en conclusión

Los resultados de la prueba anterior prueban que el método interno modificado se agregará a la transacción del método periférico cuando el método periférico inicie la transacción . Todos los métodos internos modificados y los métodos periféricos pertenecen a la misma transacción . Siempre que se revierta un método. , se revertirá toda la transacción .Propagation.REQUIREDPropagation.REQUIRED

2, PROPAGACIÓN_REQUIERE_NUEVO

Usuario1Servicio

Agregue 1 método, comportamiento de propagación de transacciones: REQUIRES_NEW

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void requires_new(String name) {
    
    
    this.jdbcTemplate.update("insert into user1(name) VALUES (?)", name);
}

Usuario2Servicio

Agregue 2 métodos, comportamiento de propagación de transacciones: REQUIRES_NEW, tenga en cuenta que la última línea dentro del segundo método generará una excepción.

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void requires_new(String name) {
    
    
    this.jdbcTemplate.update("insert into user2(name) VALUES (?)", name);
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void requires_new_exception(String name) {
    
    
    this.jdbcTemplate.update("insert into user2(name) VALUES (?)", name);
    throw new RuntimeException();
}

Escenario 1 (2-1)

Los métodos periféricos no tienen transacciones .

Método de verificación 1

Agregar Servicio Tx
public void notransaction_exception_requiresNew_requiresNew(){
    
    
    user1Service.requires_new("张三");
    user2Service.requires_new("李四");
    throw new RuntimeException();
}
Agregado en Demo6Test
@Test
public void notransaction_exception_requiresNew_requiresNew() {
    
    
    txService.notransaction_exception_requiresNew_requiresNew();
}
ejecutar salida
user1表数据:[{id=1, name=张三}]
user2表数据:[{id=1, name=李四}]

Método de verificación 2

Agregar Servicio Tx
public void notransaction_requiresNew_requiresNew_exception(){
    
    
    user1Service.requires_new("张三");
    user2Service.requires_new_exception("李四");
}
Caso de prueba, agregado en Demo6Test
@Test
public void notransaction_requiresNew_requiresNew_exception() {
    
    
    txService.notransaction_requiresNew_requiresNew_exception();
}
ejecutar salida
user1表数据:[{id=1, name=张三}]
user2表数据:[]

Análisis de resultados

Número de serie del método de verificación resultado de la base de datos Análisis de resultados
1 Inserciones "Zhang San", inserciones "Li Si". El método periférico no tiene transacción, y los métodos de inserción de "Zhang San" y "Li Si" se ejecutan de forma independiente en sus propias transacciones, y la reversión de una excepción lanzada por el método periférico no afectará al método interno.
2 Se inserta "Zhang San", no se inserta "Li Si" El método periférico no abre la transacción, el método de insertar "Zhang San" y el método de insertar "Li Si" abren sus propias transacciones respectivamente, y el método de insertar "Li Si" arroja una excepción y retrocede, y otros las transacciones no se ven afectadas.

en conclusión

A través de estos dos métodos, demostramos que el método interno modificado abrirá su propia transacción cuando el método periférico no abra la transacción , y las transacciones abiertas son independientes entre sí y no interfieren entre sí.Propagation.REQUIRES_NEW

Escena 2 (2-2)

El método periférico inicia la transacción .

Método de verificación 1

Agregar Servicio Tx
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_exception_required_requiresNew_requiresNew() {
    
    
    user1Service.required("张三");

    user2Service.requires_new("李四");

    user2Service.requires_new("王五");
    throw new RuntimeException();
}
Caso de prueba, agregado en Demo6Test
@Test
public void transaction_exception_required_requiresNew_requiresNew() {
    
    
    txService.transaction_exception_required_requiresNew_requiresNew();
}
ejecutar salida
user1表数据:[]
user2表数据:[{id=1, name=李四}, {id=2, name=王五}]

Método de verificación 2

Agregar Servicio Tx
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_required_requiresNew_requiresNew_exception() {
    
    
    user1Service.required("张三");

    user2Service.requires_new("李四");

    user2Service.requires_new_exception("王五");
}
Agregado en Demo6Test
@Test
public void transaction_required_requiresNew_requiresNew_exception() {
    
    
    txService.transaction_required_requiresNew_requiresNew_exception();
}
ejecutar salida
user1表数据:[]
user2表数据:[{id=1, name=李四}]

Método de verificación 3

Agregar Servicio Tx
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_required_requiresNew_requiresNew_exception_try(){
    
    
    user1Service.required("张三");

    user2Service.requires_new("李四");

    try {
    
    
        user2Service.requires_new_exception("王五");
    } catch (Exception e) {
    
    
        System.out.println("回滚");
    }
}
Agregado en Demo6Test
@Test
public void transaction_required_requiresNew_requiresNew_exception_try() {
    
    
    txService.transaction_required_requiresNew_requiresNew_exception_try();
}
ejecutar salida
回滚
user1表数据:[{id=1, name=张三}]
user2表数据:[{id=1, name=李四}]

Análisis de resultados

Número de serie del método de verificación resultado de la base de datos Análisis de resultados
1 No se inserta "Zhang San", se inserta "Li Si" y se inserta "Wang Wu". El método periférico inicia la transacción, inserta el método "Zhang San" y el método periférico en una transacción, inserta el método "Li Si" e inserta el método "Wang Wu" en nuevas transacciones separadas, y el método periférico genera una excepción. y solo revierte lo mismo que el método periférico El método de la transacción, por lo que se revierte el método de inserción de "Zhang San".
2 No se inserta "Zhang San", se inserta "Li Si" y no se inserta "Wang Wu". El método periférico abre la transacción, inserta el método "Zhang San" y una transacción del método periférico, inserta el método "Li Si" e inserta el método "Wang Wu" en nuevas transacciones independientes. La inserción del método "Wang Wu" arroja una excepción, la transacción insertada en el método "Wang Wu" se revierte primero, y la excepción continúa arrojándose y es detectada por el método periférico, y la transacción del método periférico también se revierte , por lo que el método "Zhang San" insertado también se retrotrae.
3 Se inserta "Zhang San", se inserta "Li Si" y no se inserta "Wang Wu". El método periférico abre la transacción, inserta el método "Zhang San" y una transacción del método periférico, inserta el método "Li Si" e inserta el método "Wang Wu" en nuevas transacciones independientes. Al insertar el método "Wang Wu" se genera una excepción. La transacción insertada en el método "Wang Wu" se revierte primero. La excepción se detecta y el método periférico no la detectará, y la transacción del método periférico no se revierte. por lo que la inserción del método "Zhang San" se inserta con éxito.

en conclusión

Cuando el método periférico abre la transacción, el Propagation.REQUIRES_NEWmétodo interno modificado aún abrirá la transacción independiente por separado, y también es independiente de la transacción del método externo. Los métodos internos, el método interno y la transacción del método externo son independientes entre sí y no no interferir entre sí.

3, PROPAGACIÓN_ANIDADO

Usuario1Servicio

Agregue 1 método, comportamiento de propagación de transacciones: NESTED

@Transactional(propagation = Propagation.NESTED)
public void nested(String name) {
    
    
    this.jdbcTemplate.update("insert into user1(name) VALUES (?)", name);
}

Usuario2Servicio

Agregue 2 métodos, comportamiento de propagación de transacciones: NESTED, tenga en cuenta que la última línea dentro del segundo método generará una excepción.

@Transactional(propagation = Propagation.NESTED)
public void nested(String name) {
    
    
    this.jdbcTemplate.update("insert into user2(name) VALUES (?)", name);
}

@Transactional(propagation = Propagation.NESTED)
public void nested_exception(String name) {
    
    
    this.jdbcTemplate.update("insert into user2(name) VALUES (?)", name);
    throw new RuntimeException();
}

Escenario 1 (3-1)

Los métodos periféricos no tienen transacciones .

Método de verificación 1

Agregar Servicio Tx
public void notransaction_exception_nested_nested(){
    
    
    user1Service.nested("张三");
    user2Service.nested("李四");
    throw new RuntimeException();
}
Agregado en Demo6Test
@Test
public void notransaction_exception_nested_nested() {
    
    
    txService.notransaction_exception_nested_nested();
}
ejecutar salida
user1表数据:[{id=1, name=张三}]
user2表数据:[{id=1, name=李四}]

Método de verificación 2

Agregar Servicio Tx
public void notransaction_nested_nested_exception(){
    
    
    user1Service.nested("张三");
    user2Service.nested_exception("李四");
}
Caso de prueba, agregado en Demo6Test
@Test
public void notransaction_nested_nested_exception() {
    
    
    txService.notransaction_nested_nested_exception();
}
ejecutar salida
user1表数据:[{id=1, name=张三}]
user2表数据:[]

Análisis de resultados

Número de serie del método de verificación resultado de la base de datos Análisis de resultados
1 Se insertan tanto "Zhang San" como "Li Si". El método periférico no abre la transacción, y los métodos insertados "Zhang San" y "Li Si" se ejecutan de forma independiente en su propia transacción, y la anomalía del método periférico no afecta la transacción independiente de la inserción interna del "Zhang Métodos San" y "Li Si".
2 Se inserta "Zhang San", pero no se inserta "Li Si". El método periférico no tiene transacción, y los métodos de inserción de "Zhang San" y "Li Si" se ejecutan de forma independiente en sus propios asuntos, por lo que si el método de inserción de "Li Si" produce una excepción, solo el método de inserción de "Li Si " se revertirá y el método de inserción de "Zhang San" " no se verá afectado.

en conclusión

A través de estos dos métodos, demostramos que la función es la misma cuando el método periférico no abre la transacciónPropagation.NESTED , y Propagation.REQUIREDel método interno modificado abrirá su propia transacción, y las transacciones abiertas son independientes entre sí y no interfieren entre sí. .

Escenario 2 (3-1)

El método periférico inicia la transacción .

Método de verificación 1

Agregar Servicio Tx
@Transactional
public void transaction_exception_nested_nested(){
    
    
    user1Service.nested("张三");
    user2Service.nested("李四");
    throw new RuntimeException();
}
Caso de prueba, agregado en Demo6Test
@Test
public void transaction_exception_nested_nested() {
    
    
    txService.transaction_exception_nested_nested();
}
ejecutar salida
user1表数据:[]
user2表数据:[]

Método de verificación 2

Agregar Servicio Tx
@Transactional
public void transaction_nested_nested_exception(){
    
    
    user1Service.nested("张三");
    user2Service.nested_exception("李四");
}
Agregado en Demo6Test
@Test
public void transaction_nested_nested_exception() {
    
    
    txService.transaction_nested_nested_exception();
}
ejecutar salida
user1表数据:[]
user2表数据:[]

Método de verificación 3

Agregar Servicio Tx
@Transactional
public void transaction_nested_nested_exception_try(){
    
    
    user1Service.nested("张三");
    try {
    
    
        user2Service.nested_exception("李四");
    } catch (Exception e) {
    
    
        System.out.println("方法回滚");
    }
}
Agregado en Demo6Test
@Test
public void transaction_nested_nested_exception_try() {
    
    
    txService.transaction_nested_nested_exception_try();
}
ejecutar salida
方法回滚
user1表数据:[{id=1, name=张三}]
user2表数据:[]

Análisis de resultados

Número de serie del método de verificación resultado de la base de datos Análisis de resultados
1 "Zhang San" y "Li Si" no están insertados. El método periférico inicia la transacción, la transacción interna es una subtransacción de la transacción periférica, el método periférico retrocede y el método interno también retrocede.
2 "Zhang San" y "Li Si" no están insertados. El método periférico inicia la transacción, la transacción interna es una subtransacción de la transacción periférica, el método interno lanza una excepción y retrocede, y el método periférico percibe la excepción y hace que la transacción general retroceda.
3 Se inserta "Zhang San", no se inserta "Li Si". El método periférico inicia la transacción, y la transacción interna es una subtransacción de la transacción periférica. La inserción del método interno "Li Si" genera una excepción, y la subtransacción se puede revertir por separado.

en conclusión

Los resultados de la prueba anterior demuestran que cuando el método periférico inicia la transacción,Propagation.NESTED el método interno modificado pertenece a la subtransacción de la transacción externa, la transacción principal periférica se revierte, la subtransacción se debe revertir y la subtransacción interna se puede revertir sola. sin afectar la transacción principal periférica y otras subtransacciones.

Principios de Asuntos Internos

Tome mysql como ejemplo, hay una función savepoint en mysql , y los asuntos internos de NESTED se realizan a través de esto.

REQUERIDO, REQUIRES_NUEVO, comparación ANIDADO

De la comparación de "Escena 2 (1-2)" y "Escena 2 (3-2)", podemos saber que:

REQUIRED和NESTED修饰的内部方法都属于外围方法事务,如果外围方法抛出异常,这两种方法的事务都会被回滚。但是REQUIRED是加入外围方法事务,所以和外围事务同属于一个事务,一旦REQUIRED事务抛出异常被回滚,外围方法事务也将被回滚。而NESTED是外围方法的子事务,有单独的保存点,所以NESTED方法抛出异常被回滚,不会影响到外围方法的事务。

由“场景2(2-2)”和“场景2(3-2)”对比,我们可知:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WAyfqjLJ-1684551155559)(%E6%96%B0%E5%BB%BA%E6%96%87%E6%9C%AC%E6%96%87%E6%A1%A3/1369022-20211106180800178-1711946829.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V8XmoxTU-1684551155560)(%E6%96%B0%E5%BB%BA%E6%96%87%E6%9C%AC%E6%96%87%E6%A1%A3/1369022-20211106180849305-943220982.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-USUfodFj-1684551155561)(%E6%96%B0%E5%BB%BA%E6%96%87%E6%9C%AC%E6%96%87%E6%A1%A3/1369022-20211106180830624-134505303.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SN2OTtcT-1684551155561)(%E6%96%B0%E5%BB%BA%E6%96%87%E6%9C%AC%E6%96%87%E6%A1%A3/1369022-20211106180938009-117156703.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rAjSgTjR-1684551155561)(%E6%96%B0%E5%BB%BA%E6%96%87%E6%9C%AC%E6%96%87%E6%A1%A3/1369022-20211106180953006-1582120138.png)]

方法开启事务,内部事务为外围事务的子事务,插入“李四”内部方法抛出异常,可以单独对子事务回滚。 |

结论

以上试验结果我们证明在外围方法开启事务的情况下Propagation.NESTED修饰的内部方法属于外部事务的子事务,外围主事务回滚,子事务一定回滚,而内部子事务可以单独回滚而不影响外围主事务和其他子事务。

内部事务原理

以mysql为例,mysql中有个savepoint的功能,NESTED内部事务就是通过这个实现的。

REQUIRED,REQUIRES_NEW,NESTED比较

由“场景2(1-2)”和“场景2(3-2)”对比,我们可知:

REQUIRED和NESTED修饰的内部方法都属于外围方法事务,如果外围方法抛出异常,这两种方法的事务都会被回滚。但是REQUIRED是加入外围方法事务,所以和外围事务同属于一个事务,一旦REQUIRED事务抛出异常被回滚,外围方法事务也将被回滚。而NESTED是外围方法的子事务,有单独的保存点,所以NESTED方法抛出异常被回滚,不会影响到外围方法的事务。

由“场景2(2-2)”和“场景2(3-2)”对比,我们可知:

[外链图片转存中…(img-WAyfqjLJ-1684551155559)][外链图片转存中…(img-V8XmoxTU-1684551155560)]

[外链图片转存中…(img-USUfodFj-1684551155561)]

[外链图片转存中…(img-SN2OTtcT-1684551155561)]

[外链图片转存中…(img-rAjSgTjR-1684551155561)]

来源;http://www.itsoku.com/course/5/127

Supongo que te gusta

Origin blog.csdn.net/china_coding/article/details/130779172
Recomendado
Clasificación