Spring 实现事务 代码实战(注解开发)

导读:本篇文章讲解 Spring 实现事务 代码实战(注解开发),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

目录

Spring 事务是什么?

什么是事务传播?

相关代码 参见 (0积分下载):

java代码

PROPAGATION_REQUIRED

PROPAGATION_REQUIRES_NEW

错误


 

Spring 事务是什么?

在使用Spring框架时,可以有两种使用事务的方式,一种是编程式的,一种是声明式的,@Transactionali注解就是声明式的。

首先,事务这个概念是数据库层面的,Spring只是基于数据库中的事务进行了扩展,以及提供了一些能让程序员更加方便操作事务的方式。比如我们可以通过在某个方法上增加@Transactional注解,就可以开启事务,这个方法中所有的sql都会在一个事务中执行,统一成功或失败。比如

增加钱和减少钱 作为一个事务,要么都成功 要么都失败。

public class ServiceB {
    @Autowired
    private UserDao userDao;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB(){
        userDao.addMoney();
        int a = 2/0;
        userDao.reduceMoney();
    }
}

在一个方法上加了@Transactional注解后,Spring会基于这个类生成一个代理对象,会将这个代理对象作为bean,当在使用这个代理对象的方法时,如果这个方法上存在@Transactional注解,那么代理逻辑会先把事务的自动提交设置为false,然后再去执行原本的业务逻辑方法,如果执行业务逻辑方法没有出现异常,那么代理逻辑中就会将事务进行提交,如果执行业务逻辑方法出现了异常,那么则会将事务进行回滚。

什么是事务传播?

所谓事务传播机制,指的就是 方法A带有事务,去调用带有事务的方法B,方法A,B事务设置的事务属性,会对方法A,B的事务产生什么影响。前提:A调用B A是父事务,B是子事务。

所谓事务的嵌套就是两个事务方法之间相互调用。spring事务开启 ,或者是基于接口的或者是基于类的代理被创建( 注意一定要是代理,不能手动new 一个对象,并且此类(有无接口都行)一定要被代理——spring中的bean只要纳入了IOC管理都是被代理的 )。所以在同一个类中一个方法调用另一个方法有事务的方法,事务是不会起作用的。
深入理解事务–Spring事务的传播机制_yuanlaishini2010的博客-CSDN博客_spring的事务传播

一共有7种传播机制,这里列举最常见的两种,附上代码。

举例解析 摘自上述博客

相关代码 参见 (0积分下载):

spring事务传播demo-Java文档类资源-CSDN下载

或者gitee

springTransactionDemo: 帮助理解 spring 的事务传播。

数据库sql:

随便设计一张表即可。

Spring 实现事务 代码实战(注解开发)

java代码

@Service
public class ServiceB {
    @Autowired
    private UserDao userDao;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB(){
//        userDao.addMoney();
        // 如果methodB的事务传播设置为 REQUIRES_NEW ,那么在本例子中会产生死锁  所以单设了一张表
        // 用来模拟REQUIRES_NEW的情况
        userDao.insert(); // 在另一张表中插入.
        int a = 2/0;
//        userDao.reduceMoney();
    }
}


@Service
public class ServiceA {
    @Autowired
    private UserDao userDao;
    @Autowired
    private ServiceB serviceB;

//    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA(){
        userDao.reduceMoney();
        serviceB.methodB();
    }
}



public class TestPropagation {
    ApplicationContext context =
            new ClassPathXmlApplicationContext("bean1.xml");
    ServiceA serviceA = context.getBean("serviceA", ServiceA.class);
    @Test
    public void testRequiredDemo(){
        serviceA.methodA();
    }
}

ServiceA#methodA,ServiceB#methodB
 

    ServiceA {
           
         void methodA() {
             ServiceB.methodB();
         }
      
    }
      
    ServiceB {
           
         void methodB() {
         }
           
    }

PROPAGATION_REQUIRED

假如当前正要执行的事务不在另外一个事务里,那么就起一个新的事务
比如说,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED, 那么由于执行ServiceA.methodA的时候
  1、如果ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA的事务内部,就不再起新的事务。这时只有外部事务并且他们是共用的,所以这时ServiceA.methodA或者ServiceB.methodB无论哪个发生异常methodA和methodB作为一个整体都将一起回滚。
  2、如果ServiceA.methodA没有事务,ServiceB.methodB就会为自己分配一个事务。这样,在ServiceA.methodA中是没有事务控制的。只是在ServiceB.methodB内的任何地方出现异常,ServiceB.methodB将会被回滚,不会引起ServiceA.methodA的回滚

PROPAGATION_REQUIRES_NEW

启动一个新的, 不依赖于环境的 “内部” 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行.
 比如我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW,那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务,等待ServiceB.methodB的事务完成以后,他才继续执行。他与PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为ServiceB.methodB是新起一个事务,那么就是存在两个不同的事务。
1、如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。
2、如果ServiceB.methodB失败回滚,如果他抛出的异常被ServiceA.methodA的try..catch捕获并处理,ServiceA.methodA事务仍然可能提交;如果他抛出的异常未被ServiceA.methodA捕获处理,ServiceA.methodA事务将回滚。
 

带你读懂Spring 事务——事务的传播机制 – 知乎Spring 实现事务 代码实战(注解开发)https://zhuanlan.zhihu.com/p/148504094

错误

在A事务使用 required, B事务使用requires_new的时候,会出现:

Lock wait timeout exceeded; try restarting transaction;

这是因为 事务A里面的sql还没提交,就进了事务B,所以加了锁,里面的事务B操作数据时也要拿不到锁就会产生这个问题。所以又加了一张表用来测试。

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB(){
//        userDao.addMoney();
        // 如果methodB的事务传播设置为 REQUIRES_NEW ,那么在本例子中会产生死锁  所以单设了一张表
        // 用来模拟REQUIRES_NEW的情况
        userDao.insert(); // 在另一张表中插入.
        int a = 2/0;
//        userDao.reduceMoney();
    }

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之家整理,本文链接:https://www.bmabk.com/index.php/post/92800.html

(0)
小半的头像小半

相关推荐

极客之家——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!