Spring implementa la gestión de transacciones basada en anotaciones

En la sección "Administración de transacciones basada en XML en Spring", simplificamos enormemente la configuración XML requerida para las transacciones declarativas de Spring a través del elemento tx:advice. Pero, de hecho, podemos simplificarlo aún más de otra manera, que es "usar anotaciones para implementar la gestión de transacciones".

En Spring, además de XML, las transacciones declarativas también se pueden implementar mediante anotaciones para reducir aún más el acoplamiento entre códigos. Presentemos cómo implementar la gestión de transacciones declarativas a través de anotaciones.

  1. Transacción de anotación abierta
    El espacio de nombres tx proporciona un elemento controlado por anotación tx:, que se utiliza para abrir la transacción de anotación y simplificar la configuración XML de la transacción declarativa de Spring.

El uso del elemento tx:annotation-driven también es muy simple, solo necesitamos agregar dicha línea de configuración en la configuración Spring XML.
<tx:administrador de transacciones controlado por anotaciones="administrador de transacciones"></tx:controlado por anotaciones>

Al igual que el elemento tx:advice, tx:annotation-driven también necesita definir un administrador de transacciones a través del atributo de administrador de transacciones, y el valor predeterminado de este parámetro es transactionManager. Si el id del administrador de transacciones que usamos es el mismo que el valor predeterminado, se puede omitir la configuración de esta propiedad, y el formulario es el siguiente.
tx:controlado por anotaciones/

Después de habilitar la transacción de anotación a través del elemento tx:annotation-driven, Spring verificará automáticamente los beans en el contenedor, encontrará los beans anotados con @Transactional y proporcionará soporte de transacciones para ellos.
2. Use la anotación @Transactional
La anotación @Transactional es la anotación central de la programación de transacciones declarativas de Spring, que se puede usar tanto en clases como en métodos.
@Transactional
public class XXX { @Transactional public void A(Order order) { ... } public void B(Order order) { ... } }







Si la anotación @Transactional se usa en una clase, significa que todos los métodos de la clase admiten transacciones; si la anotación @Transactional se usa en un método, significa que el método actual admite transacciones.

Spring encuentra todos los beans anotados con @Transactional en el contenedor y automáticamente les agrega notificaciones de transacción. Los atributos de transacción de la notificación se definen a través de los atributos de la anotación @Transactional.

La anotación @Transactional contiene múltiples atributos, entre los cuales los atributos comunes son los siguientes.
inserte la descripción de la imagen aquí
Ejemplo 1
A continuación, usaremos un ejemplo para demostrar cómo implementar transacciones declarativas a través de anotaciones.Los pasos son los siguientes.

  1. Cree una nueva instancia de base de datos llamada spring-tx-db en la base de datos MySQL y ejecute la siguiente instrucción SQL en esta base de datos.

DROP TABLE SI EXISTE account;
CREATE TABLE account(
idbigint NOT NULL AUTO_INCREMENT COMMENT 'id',
user_idbigint DEFAULT NULL COMMENT 'userid',
totaldecimal(10,0) DEFAULT NULL COMMENT 'total cantidad',
useddecimal(10,0) DEFAULT NULL COMMENT ' Usado saldo',
residuedecimal(10,0) DEFAULT '0' COMENTARIO 'Cuota disponible restante',
PRIMARY KEY ( id)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
INSERTAR EN accountVALORES ('1', '1', '1000 ', '0', '1000');
DROP TABLE SI EXISTE order;
CREATE TABLE order(
idbigint NOT NULL AUTO_INCREMENT,
order_idvarchar(200) NOT NULL,
user_idvarchar(200) NOT NULL COMMENT 'userid',
product_idvarchar(200) NOT NULL COMENT ' ID del Producto',
countint DEFAULT NULL COMMENT 'cantidad',
moneydecimal (11,0) DEFAULT NULL COMMENT 'cantidad',
statusint DEFAULT NULL COMMENT 'estado del pedido: 0: creando; 1: completado',
PRIMARY KEY ( id)
) ENGINE=InnoDB DEFAULT CHARSET =utf8 ;
DROP TABLE SI EXISTE storage;
CREATE TABLE storage(
idbigint NOT NULL AUTO_INCREMENT,
product_idbigint DEFAULT NULL COMMENT 'product id',
totalint DEFAULT NULL COMMENT 'inventario total', int
usedDEFAULT NULL COMMENT 'inventario usado',
residueint DEFAULT NULL COMMENT 'inventario restante',
CLAVE PRINCIPAL ( id)
) MOTOR=InnoDB AUTO_INCREMENTO=2 CARACTERÍSTICAS PREDETERMINADAS=utf8
INSERTAR EN storageVALORES ('1', '1', '100', '0', '100');

A través de la declaración SQL anterior, creamos tres tablas de base de datos: pedido (tabla de pedidos), almacenamiento (tabla de inventario de productos básicos), cuenta (tabla de cuentas de usuario).

  1. Cree un nuevo proyecto Java llamado my-spring-tx-demo e importe las siguientes dependencias al proyecto.
    spring-beans-5.3.13.RELEASE.jar
    spring-context-5.3.13.RELEASE.jar
    spring-core
    -5.3.13.RELEASE.jar spring-expression-5.3.13.RELEASE.jar
    commons-logging-1.2. jar
    spring-jdbc-5.3.13.RELEASE.jar
    spring-tx-5.3.13.RELEASE.jar
    spring-aop-5.3.13.jar
    mysql-connector-java-8.0.23.jar
    aspecto jweaver-1.9.7.jar
    primavera-aspectos-5.3.13.jar

  2. En el paquete net.biancheng.c.entity, cree una clase de entidad llamada Orden, el código es el siguiente.
    paquete net.biancheng.c.entity;
    import java.math.BigDecimal;
    public class Order { // id de autoincremento private Long id; // id de pedido private String orderId; // id de usuario private String userId; // id de producto private String productId; //cantidad de artículo del pedido private Integer count; //cantidad del pedido private BigDecimal money; //estado del pedido private Integer status; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getOrderId() { return orderId; }























    public void setOrderId(String orderId) { this.orderId = orderId; } public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getProductId() { return productId; } public void setProductId(String productId) { this.productId = productId; } public Integer getCount() { return count; } public void setCount(Integer count) { this.count = count; } public BigDecimal getMoney() { devolver dinero; }























    public void setMoney(BigDecimal dinero) { this.money = dinero; } entero público getStatus() { estado de retorno; } public void setStatus(Integer status) { this.status = status; } }








  3. En el paquete net.biancheng.c.entity, cree una clase de entidad llamada Cuenta, el código es el siguiente.
    paquete net.biancheng.c.entity;
    import java.math.BigDecimal;
    public class Account { // ID de incremento automático private Long id; // ID de usuario private String userId; // Importe total de la cuenta private BigDecimal total; // Usado Importe de la cuenta privado BigDecimal utilizado; // Importe restante de la cuenta privado BigDecimal residuo; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; }






















    public BigDecimal getTotal() { devuelve el total; } public void setTotal(BigDecimal total) { this.total = total; } public BigDecimal getUsed() { return used; } public void setUsed(BigDecimal used) { this.used = used; } public BigDecimal getResidue() { devolver residuo; } public void setResidue(BigDecimal residuo) { this.residue = residuo; } }

















  4. En el paquete net.biancheng.c.entity, cree una clase de entidad llamada Almacenamiento, el código es el siguiente.
    package net.biancheng.c.entity;
    public class Storage { //ID de incremento automático private Long id; //Id. de producto private String productId; //Total de inventario de producto private Integer total; //Cantidad de producto usado private Integer utilizado; / /cantidad restante del producto privado Integer residuo; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getProductId() { return productId; } public void setProductId(String productId) { this.productId = productId; }






















    public Integer getTotal() { devuelve el total; } public void setTotal(Integer total) { this.total = total; } public Integer getUsed() { return used; } public void setUsed(Integer used) { this.used = used; } public Integer getResidue() { devolver residuo; } setResidue public void (residuo entero) { this.residue = residuo; } }

















  5. En el paquete net.biancheng.net.dao, cree una interfaz llamada OrderDao, el código es el siguiente.
    paquete net.biancheng.c.dao;
    import net.biancheng.c.entity.Order;
    interfaz pública OrderDao { /**

    • Crear orden
    • orden @param
    • @return
      /
      int createOrder(orden de pedido);
      /
      *
    • Modificar el estado del pedido
    • Cambiar el estado del pedido de no completado (0) a completado (1)
    • @param orderId
    • @param status
      */
      void updateOrderStatus(String orderId, Integer status);
      }
  6. En el paquete net.biancheng.net.dao, cree una interfaz llamada AccountDao, el código es el siguiente.
    paquete net.biancheng.c.dao;
    importar net.biancheng.c.entity.Account;
    importar java.math.BigDecimal;
    interfaz pública AccountDao { /**

    • Consultar el importe de la cuenta según el usuario
    • @param ID de usuario
    • @return
      /
      Account selectByUserId(String userId);
      /
      *
    • Deducir el monto de la cuenta
    • @param ID de usuario
    • @param dinero
    • @return
      */
      int decremento(String userId, BigDecimal money);
      }
  7. En el paquete net.biancheng.net.dao, cree una interfaz llamada StorageDao, el código es el siguiente.
    paquete net.biancheng.c.dao;
    importar net.biancheng.c.entity.Storage;
    interfaz pública StorageDao { /**

    • Consultar el inventario del producto.
    • @param ID de producto
    • @return
      /
      Storage selectByProductId(String productId);
      /
      *
    • Deducción del inventario de mercancías
    • registro @param
    • @return
      */
      int decremento(registro de almacenamiento);
      }
  8. 在 net.biancheng.c.dao.impl 包下,创建 OrderDao 的实现类 OrderDaoImpl,代码如下。
    paquete net.biancheng.c.dao.impl;
    importar net.biancheng.c.dao.OrderDao;
    importar net.biancheng.c.entity.Order;
    importar org.springframework.beans.factory.annotation.Autowired;
    importar org.springframework.jdbc.core.JdbcTemplate;
    importar org.springframework.stereotype.Repository;
    @Repository
    clase pública OrderDaoImpl implementa OrderDao { @Autowired private JdbcTemplate jdbcTemplate; @Override public int createOrder(Pedido de pedido) { String sql = “insertar en (order_id,user_id, product_id, , money, status) valores (?,?,?,?,?,?)”;




    ordercount
    int update = jdbcTemplate.update(sql, order.getOrderId(), order.getUserId(), order.getProductId(), order.getCount(), order.getMoney(), order.getStatus());
    actualización de retorno;
    }
    @Override
    public void updateOrderStatus(String orderId, Integer status) { String sql = " update set status = 1 where order_id = ? and status = ?;"; jdbcTemplate.update(sql, orderId, estado); } }
    order


  9. 在 net.biancheng.c.dao.impl 包下,创建 AccountDao 的实现类 AccountDaoImpl,代码如下。
    paquete net.biancheng.c.dao.impl;
    importar net.biancheng.c.dao.AccountDao;
    importar net.biancheng.c.entity.Account;
    importar net.biancheng.c.entity.Order;
    importar org.springframework.beans.factory.annotation.Autowired;
    importar org.springframework.jdbc.core.BeanPropertyRowMapper;
    importar org.springframework.jdbc.core.JdbcTemplate;
    importar org.springframework.stereotype.Repository;
    importar java.math.BigDecimal;
    @Repository
    clase pública AccountDaoImpl implementa AccountDao { @Autowired private JdbcTemplate jdbcTemplate; @Override public Account selectByUserId(String userId) {




    String sql = "seleccionar * de la cuenta donde id_usuario =?";
    return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper(Account.class), userId);
    }
    @Override
    public int decremento(String userId, BigDecimal money) { String sql = “ACTUALIZAR cuenta SET residuo = residuo - ?, usado = usado + ? DONDE id_usuario = ?;”; volver jdbcTemplate.update(sql, dinero, dinero, ID de usuario); } }



  10. 在 net.biancheng.c.dao.impl 包下,创建 StorageDao 的实现类 StorageDaoImpl,代码如下。
    paquete net.biancheng.c.dao.impl;
    importar net.biancheng.c.dao.StorageDao;
    importar net.biancheng.c.entity.Storage;
    importar org.springframework.beans.factory.annotation.Autowired;
    importar org.springframework.jdbc.core.BeanPropertyRowMapper;
    importar org.springframework.jdbc.core.JdbcTemplate;
    importar org.springframework.stereotype.Repository;
    @Repository
    clase pública StorageDaoImpl implementa StorageDao { @Autowired private JdbcTemplate jdbcTemplate; @Override public Storage selectByProductId(String productId) { String sql = “select * from storage where product_id = ?”;





    return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper(Storage.class), productId);
    }
    @Override
    public int decremento(Registro de almacenamiento) { String sql = "actualizar conjunto de almacenamiento utilizado =? ,residuo=? where product_id=?"; return jdbcTemplate.update(sql, record.getUsed(), record.getResidue(), record.getProductId()); } }



  11. En el paquete net.biancheng.c.service, cree una interfaz llamada OrderService, el código es el siguiente.
    paquete net.biancheng.c.service;
    import net.biancheng.c.entity.Order;
    interfaz pública OrderService { /**

    • Crear orden
    • orden @param
    • @return
      */
      public void createOrder(orden de pedido);
      }
  12. 在 net.biancheng.c.service.impl 包下,创建 OrderService 的实现类 OrderServiceImpl,代码如下。
    paquete net.biancheng.c.service.impl;
    importar net.biancheng.c.dao.AccountDao;
    importar net.biancheng.c.dao.OrderDao;
    importar net.biancheng.c.dao.StorageDao;
    importar net.biancheng.c.entity.Account;
    importar net.biancheng.c.entity.Order;
    importar net.biancheng.c.entity.Storage;
    importar net.biancheng.c.service.OrderService;
    importar org.springframework.beans.factory.annotation.Autowired;
    importar org.springframework.stereotype.Service;
    importar org.springframework.transaction.annotation.Isolation;
    importar org.springframework.transaction.annotation.Propagation;
    importar org.springframework.transaction.annotation.Transactional;
    importar java.texto.SimpleDateFormat;
    importar java.util.Date;
    @Service(“orderService”)
    public class OrderServiceImpl implements OrderService { @Autowired private OrderDao orderDao; @Autowired privado AccountDao cuentaDao; @Autowired privado StorageDao storageDao; /**






    • Use la anotación @Transactional en el método,
    • @param order
      */
      @Transactional(isolation = Isolation.DEFAULT, propagación = Propagation.REQUIRED, timeout = 10, readOnly = false)
      @Override
      public void createOrder(Pedido de pedido) { //Generar automáticamente el id de pedido SimpleDateFormat df = new SimpleDateFormat( "yyyyMMddHHmmssSSS"); String format = df.format(new Date()); String orderId = order.getUserId() + order.getProductId() + format; System.out.println("El ID de pedido generado automáticamente es:" + orderId); order.setOrderId(orderId); System.out.println("Comenzar a crear datos de pedido, el número de pedido es: " + orderId); //Crear datos de pedido orderDao.createOrder(order); System.out.println ("Se crean los datos del pedido, el número de pedido es:" + orderId); System.out.println("Empiece a consultar el inventario del producto, el id del producto es: " + order.getProductId());











      Almacenamiento almacenamiento = storageDao.selectByProductId(order.getProductId());
      if (storage != null && storage.getResidue().intValue() >= order.getCount().intValue()) { System.out.println(" El inventario de productos es suficiente y se deduce el inventario de productos"); almacenamiento.setUsed(almacenamiento.getUsed() + pedido.getCount()); almacenamiento.setResidue(almacenamiento.getTotal().intValue() - almacenamiento.getUsed ()); int decremento = storageDao.decrease(almacenamiento); System.out.println("Deducción del inventario de productos básicos completada"); } else { System.out.println("Advertencia: Inventario insuficiente, ¡la operación de reversión está en progreso!" ); throw new RuntimeException("Inventario insuficiente"); } System.out.println("Empezar a consultar el monto de la cuenta del usuario"); Cuenta de cuenta = accountDao.selectByUserId(order.getUserId());











      if (cuenta != nulo && cuenta.getResidue().intValue() >= order.getMoney().intValue()) { System.out.println("El monto de la cuenta es suficiente y el monto de la cuenta se está deduciendo" ); accountDao.disminución(order.getUserId(), order.getMoney()); System.out.println("Deducción de cuenta completada"); } else { System.out.println("Advertencia: Saldo de cuenta insuficiente, realizando rollback ¡Operación!"); throw new RuntimeException("Saldo de cuenta insuficiente"); } System.out.println("Empezar a modificar el estado del pedido, no completado》》》》》); orderDao.updateOrderStatus(order.getOrderId() , 0); System.out.println("¡Modificación del estado del pedido completada!"); } }











  13. En el directorio src, cree un archivo de configuración jdbc.properties, el contenido de la configuración es el siguiente.
    jdbc.driver=com.mysql.cj.jdbc.Driver
    jdbc.url=jdbc:mysql://127.0.0.1:3306/spring-tx-db
    jdbc.username=root
    jdbc.password=root

  14. Cree un archivo de configuración XML Beans.xml en el directorio src, el contenido de la configuración es el siguiente.

<?versión xml="1.0" codificación="UTF-8"?>



tx:annotation-driven/

<context:component-scan base-package=“net.biancheng.c”></context:component-scan>

<context:property-placeholder location=“classpath:jdbc.properties”></ context:propiedad-marcador de posición>




















  1. En el paquete net.biancheng.c, cree una clase llamada MainApp, el código es el siguiente.
    paquete net.biancheng.c;
    importar net.biancheng.c.entity.Order;
    importar net.biancheng.c.service.OrderService;
    importar org.springframework.context.ApplicationContext;
    importar org.springframework.context.support.ClassPathXmlApplicationContext;
    importar java.math.BigDecimal;
    public class MainApp { public static void main(String[] args) { ApplicationContext context2 = new ClassPathXmlApplicationContext(“Beans.xml”); OrderService orderService = context2.getBean(“orderService”, OrderService.class); Order order = new Order(); //Establecer id de producto order.setProductId("1"); //Cantidad de producto order.setCount(30);








    //Cantidad de la mercancía
    order.setMoney(new BigDecimal(600));
    //Establecer ID de usuario
    order.setUserId("1");
    //El estado del pedido es incompleto
    order.setStatus(0);
    orderService.createOrder(order);
    }
    }

  2. Ejecute el método principal en la clase MainApp y la salida de la consola es la siguiente.
    El ID de pedido generado automáticamente es: 1120220111173635296
    Comience a crear datos de pedido, el número de pedido es: 1120220111173635296 Se crean los datos de pedido, el número de pedido es: 1120220111173635296
    Comience
    a verificar el inventario de productos, el ID de productos es: 1
    El inventario de productos es suficiente , y el
    inventario de productos básicos se está deduciendo. Resta completada . Comience a consultar el
    monto de la cuenta del usuario
    . El monto de la cuenta es suficiente y se está deduciendo
    . ¡Modificar el estado del pedido completado!

  3. Vea los datos en la tabla de pedidos, la tabla de almacenamiento y la tabla de cuentas respectivamente, y los resultados son los siguientes.
    inserte la descripción de la imagen aquí

  4. Vuelva a ejecutar el método principal en MainApp y la salida de la consola será la siguiente.
    El ID de pedido generado automáticamente es: 1120220111175556986
    Comience a crear datos de pedido, el número de pedido es: 1120220111175556986 Se crean los datos de pedido, el número de pedido es: 1120220111175556986
    Comience
    a verificar el inventario de productos, el ID de productos es: 1
    El inventario de productos es suficiente , y el
    inventario de productos básicos se está deduciendo. La resta se completó
    . Comience a consultar el monto de la cuenta del usuario.
    Advertencia: ¡El saldo de la cuenta es insuficiente y se está realizando la operación de reversión!
    Excepción en el subproceso "principal" java.lang.RuntimeException: Saldo de cuenta insuficiente
    en net.biancheng.c.service.impl.OrderServiceImpl.createOrder(OrderServiceImpl.java:61)
    en sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    en sun .reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    en sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    en java.lang.reflect.Method.invoke(Method.java:498)
    en org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
    en org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
    en org.springframework.aop.framework.ReflectiveMethodInvocation.proceed (ReflectiveMethodInvocation.java:163)
    en org.springframework.transaction.interceptor.TransactionInterceptor1. Proceda con la invocación (Interceptor de transacciones. Java: 123) atorg. estructura de resorte. transacción interceptor Soporte de aspecto de transacción. invoque dentro de la transacción ( soporte de aspecto de transacción . java : 388 ) atorg . estructura de resorte. transacción interceptor Interceptor de transacciones. invocar (interceptor de transacciones. Java: 119) atorg. estructura de resorte. aop. estructura . Invocación del método reflexivo. proceder (Invocación del método reflexivo. java: 186) atorg. estructura de resorte. aop. interceptor E xpose Invocación I nterceptor. invocar (Exponer I nterceptor de Invocación. Java: 97) atorg. estructura de resorte. aop. estructura . Invocación del método reflexivo. proceder (Invocación del método reflexivo. java: 186) atorg. estructura de resorte. aop. estructura . J dk Dynamic A op P roxy . invoque ( Jdk Dynamic A op P roxy . java : 215 ) atcom . sol . apoderado 1. Proceda con la invocación (TransactionInterceptor.java:123) en org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388) en org.springframework.transaction.interceptor.TransactionInterceptor.1 . Proceda con I nv o c a c i ó n ( T r a n s a c c i ó n I n t e r c e p t o r . j a v a _ _ _ _ _ _ _ _ _ _:1 2 3 ) a t o r g . marco de primavera . _ _ _ _ _ _ _ _ _ _ _ _ t r a n s a c c i ó n . i n t e r c e p t o r . T r a n s a c c i ó nA p e c to S o p o r t o . _ invocar dentro de T r a n s a c t i o n ( T r a n s a c t i o n A s p e c t Sup p o r _ _ _ _ _ _ _ _ _ _ _t . j a v a:3 8 8 ) a t o r g . marco de primavera . _ _ _ _ _ _ _ _ _ _ _ _ t r a n s a c c i ó n . i n t e r c e p t o r . T r a n s a c c i ó nInterceptor . _ _ _ _ _ _ _ _ _ _ i n v o k e ( T r a n s a c c i o n I n t e r c e p t o r . java _ _ _:1 1 9 ) a t o r g . marco de primavera . _ _ _ _ _ _ _ _ _ _ _ _ una op . _ marco de trabajo . _ _ _ _ _ _ R e f l e c t i v e M é t o d o I n v o ca c i ó n . p r o c e d ( R e f l e c t i v e M é t h o d I n v o c a c i ó n . j a v a _:1 8 6 ) a t o r g . marco de primavera . _ _ _ _ _ _ _ _ _ _ _ _ una op . _ i n t e r c e p t o r . E x p o s e I n v o c a c i o n I n te r c e p t o r . i n v o k e ( E x p o s e I n v o c a c i o n I n t e r c e p t o r . j a v a:9 7 ) a t o r g . marco de primavera . _ _ _ _ _ _ _ _ _ _ _ _ una op . _ marco de trabajo . _ _ _ _ _ _ M e t o d o R e f l e c t i v a I n v o c ation _ _ _ _ p r o c e d ( R e f l e c t i v e M é t h o d I n v o c a c i ó n . j a v a _:1 8 6 ) a t o r g . marco de primavera . _ _ _ _ _ _ _ _ _ _ _ _ una op . _ marco de trabajo . _ _ _ _ _ _ J d k D y n a m i c A o p P r o x y . yo nv o k e ( J d k D y n a m i c A o p P r o x y . j a v a:2 1 5 ) a t c o m . sol _ _ _ p r o x y . Proxy13.createOrder(Fuente desconocida)
    en net.biancheng.c.MainApp.main(MainApp.java:25)

  5. Consulte la tabla de la base de datos nuevamente y descubra que no hay cambios en las tres tablas de la base de datos, lo que indica que después de que la cuenta de deducción fuera anormal, la transacción se revirtió

Supongo que te gusta

Origin blog.csdn.net/weixin_64842782/article/details/124859715
Recomendado
Clasificación