目录
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:
随便设计一张表即可。
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 事务——事务的传播机制 – 知乎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