环境准备
sql语句
CREATE TABLE `account` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL COMMENT '账户名',
`money` double(11,2) NOT NULL COMMENT '金额',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='账号表';
初始数据 配置文件
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
mapper-locations: classpath*:/mapper
启用事务管理
@Configuration
@MapperScan("com.yzm.transactional.mapper")
@EnableTransactionManagement
public class MybatisPlusConfig {
}
目录结构 以下示例中,A称为外方法,B称为内方法 AB方法不能在同一个Service中,这跟spring的事务实现机制有关 比如A方法在AccountService类中,B方法在BccountService类中
Propagation.REQUIRED
示例 11 正常转账
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA1() {
Account xwang = getById(1);
xwang.setMoney(xwang.getMoney() - 200);
updateById(xwang);
bccountService.methodB12();
System.out.println("转账成功!");
}
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB12() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 200);
updateById(xming);
int i = 1 / 0;
}
SQL执行流程 事务正常提交,金额改变。
示例 12 外方法异常
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA1() {
Account xwang = getById(1);
xwang.setMoney(xwang.getMoney() - 200);
updateById(xwang);
bccountService.methodB12();
System.out.println("转账成功!");
int i = 1 / 0;
}
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB12() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 200);
updateById(xming);
}
外方法有异常,未显示事务提交,且金额不变,说明事务回滚了
示例 13 内方法异常
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA1() {
Account xwang = getById(1);
xwang.setMoney(xwang.getMoney() - 200);
updateById(xwang);
bccountService.methodB12();
System.out.println("转账成功!");
}
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB12() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 200);
updateById(xming);
int i = 1 / 0;
}
内方法有异常,未显示事务提交,且金额不变,说明事务回滚了
从上面三个示例可以看出: REQUIRED修饰的内外方法是在同一个事务中的,只要其中一个方法抛出异常,整个事务就会回滚。
示例 14 内方法不加 @Transactional 注解,并有异常
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA1() {
Account xwang = getById(1);
xwang.setMoney(xwang.getMoney() - 200);
updateById(xwang);
bccountService.methodB12();
System.out.println("转账成功!");
}
@Override
public void methodB12() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 200);
updateById(xming);
int i = 1 / 0;
}
结果跟 @示例 13 是一样的 内方法没有注解,但能被外方法的事务包裹,使得内外方法始终在同一事务中。
示例 15 外方法不加 @Transactional 注解,并内方法有异常
@Override
public void methodA1() {
Account xwang = getById(1);
xwang.setMoney(xwang.getMoney() - 200);
updateById(xwang);
bccountService.methodB12();
System.out.println("转账成功!");
}
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB12() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 200);
updateById(xming);
int i = 1 / 0;
}
外方法以非事务方式运行的,小王钱少了200 内方法有异常,小明钱没变,说明事务回滚了
示例 16 平级内方法
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA1() {
Account xwang = getById(1);
xwang.setMoney(xwang.getMoney() - 200);
updateById(xwang);
bccountService.methodB12();
bccountService.methodB13();
System.out.println("转账成功!");
}
@Override
public void methodB12() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 100);
updateById(xming);
}
@Override
public void methodB13() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 100);
updateById(xming);
int i = 1 / 0;
}
内方法B13有异常,金额不变,说明3个方法都在同一事物中,有异常导致整个事务回滚
如果是嵌套内方法也是一样的,都在同一事务中。 总结:
默认,最常用的;
支持事务,当前存在事务则加入,不存在就创建事务;
最外层方法必须加事务注解,里层方法可加可不加(不加注解的里层方法会被外层事务包裹);
外层方法和里层包裹的方法始终在同一事务中,只要任意一个方法抛出异常(未捕获),整个事务就会回滚。
Propagation.SUPPORTS
恢复成初始数据
示例 21 外方法REQUIRED、内方法SUPPORTS并有异常
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA2() {
Account xwang = getById(1);
xwang.setMoney(xwang.getMoney() - 200);
updateById(xwang);
bccountService.methodB22();
System.out.println("转账成功!");
}
@Override
@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public void methodB22() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 200);
updateById(xming);
int i = 1 / 0;
}
外方法创建事务,内方法(支持事务)加入该事务,使得内外方法处于同一事务中,内方法抛出异常,导致整个事务回滚,金额不变。
示例 22 外方法不加 @Transactional 注解,内方法SUPPORTS
@Override
public void methodA2() {
Account xwang = getById(1);
xwang.setMoney(xwang.getMoney() - 200);
updateById(xwang);
bccountService.methodB22();
System.out.println("转账成功!");
}
@Override
@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public void methodB22() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 200);
updateById(xming);
int i = 1 / 0;
}
外方法以非事务方式运行,小王钱少了200 内方法有异常,未显示事务提交,小明钱多了200,说明内方法没有事务,也是以非事务方式运行
示例 23 内外都是SUPPORTS,内方法有异常
@Override
@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public void methodA2() {
Account xwang = getById(1);
xwang.setMoney(xwang.getMoney() - 200);
updateById(xwang);
bccountService.methodB22();
System.out.println("转账成功!");
}
@Override
@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public void methodB22() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 200);
updateById(xming);
int i = 1 / 0;
}
内方法有异常,未显示事务提交,但是金额都变了,说明是以非事务方式运行
示例 24 外方法SUPPORTS,内方法REQUIRED,内外都有异常
@Override
@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public void methodA2() {
Account xwang = getById(1);
xwang.setMoney(xwang.getMoney() - 200);
updateById(xwang);
bccountService.methodB22();
System.out.println("转账成功!");
int i = 1 / 0;
}
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB22() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 200);
updateById(xming);
int i = 1 / 0;
}
外方法有异常,小王钱少了200,说明没有事务,以非事务方式运行 内方法有异常,小明钱没变,说明内方法自己创建了一个事务,同时也证实了外方法没有事务,不然内方法一定会加入到外方法的事务中 总结:
少见少用;
支持当前事务,有事务就加入事务,没有事务就以非事务方式运行;
单独调用或最外层方法是SUPPORTS,都是以非事务方式运行,有异常不回滚。
Propagation.MANDATORY
恢复初始数据
示例 31 外方法REQUIRED、内方法MANDATORY
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA3() {
Account xwang = getById(1);
xwang.setMoney(xwang.getMoney() - 200);
updateById(xwang);
bccountService.methodB32();
System.out.println("转账成功!");
}
@Override
@Transactional(propagation = Propagation.MANDATORY, rollbackFor = Exception.class)
public void methodB32() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 200);
updateById(xming);
int i = 1 / 0;
}
跟 示例 21 一样 外方法创建事务,内方法(支持事务)加入外方法事务,同一事务,事务回滚,金额不变。
示例 32 外方法不加 @Transactional 注解,内方法MANDATORY
@Override
public void methodA3() {
Account xwang = getById(1);
xwang.setMoney(xwang.getMoney() - 200);
updateById(xwang);
bccountService.methodB32();
System.out.println("转账成功!");
}
@Override
@Transactional(propagation = Propagation.MANDATORY, rollbackFor = Exception.class)
public void methodB32() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 200);
updateById(xming);
int i = 1 / 0;
}
外层以非事务方式运行,小王钱少了 内方法直接报错
示例 33 内外都是MANDATORY(或者内方法是REQUIRED)
都是直接报错
总结:
少见少用;
支持当前事务,有事务则加入事务,没有事务就会抛出异常,显示错误信息。
单独调用或者最外层方法是MANDATORY,就直接报错
Propagation.REQUIRES_NEW
恢复初始数据
示例 41 外方法REQUIRED、内方法REQUIRES_NEW,外方法有异常
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA4() {
Account xwang = getById(1);
xwang.setMoney(xwang.getMoney() - 200);
updateById(xwang);
bccountService.methodB42();
System.out.println("转账成功!");
int i = 1 / 0;
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void methodB42() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 200);
updateById(xming);
}
小王钱不变小明钱多了200 外方法创建外事务,内方法创建内事务,外方法有异常,只会回滚外事务,内事务正常提交。
示例 42 外方法REQUIRED、内方法REQUIRES_NEW,内方法有异常
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA4() {
Account xwang = getById(1);
xwang.setMoney(xwang.getMoney() - 200);
updateById(xwang);
bccountService.methodB42();
System.out.println("转账成功!");
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void methodB42() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 200);
updateById(xming);
int i = 1 / 0;
}
金额都不变 外方法创建外事务,无异常; 内方法创建内事务,有异常导致内事务回滚,同时也让外事务回滚
示例 43 外方法REQUIRES_NEW、内方法REQUIRED,外方法有异常
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void methodA4() {
Account xwang = getById(1);
xwang.setMoney(xwang.getMoney() - 200);
updateById(xwang);
bccountService.methodB42();
System.out.println("转账成功!");
int i = 1 / 0;
}
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB42() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 200);
updateById(xming);
}
金额都不变 外方法创建事务后,内方法就不再创建事务而是加入到外方法的事务中,同一事务,外异常,就回滚
改成内异常也是整个事务回滚。
示例 44 外内方法都是REQUIRES_NEW,外方法有异常
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void methodA4() {
Account xwang = getById(1);
xwang.setMoney(xwang.getMoney() - 200);
updateById(xwang);
bccountService.methodB42();
System.out.println("转账成功!");
int i = 1 / 0;
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void methodB42() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 200);
updateById(xming);
}
小王钱不变,小明钱凭空多了200 跟 示例 41 是一样的,内外方法都有自己的事务,外方法有异常外事务回滚,内事务不影响
示例 45 外内方法都是REQUIRES_NEW,内方法有异常
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void methodA4() {
Account xwang = getById(1);
xwang.setMoney(xwang.getMoney() - 200);
updateById(xwang);
bccountService.methodB42();
System.out.println("转账成功!");
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void methodB42() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 200);
updateById(xming);
int i = 1 / 0;
}
金额都不变 跟 示例 42 是一样的,内外方法都有自己的事务,内方法有异常导致内事务回滚,外事务也跟着回滚
示例 46 平级内方法
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void methodA4() {
Account xwang = getById(1);
xwang.setMoney(xwang.getMoney() - 200);
updateById(xwang);
bccountService.methodB42();
bccountService.methodB43();
System.out.println("转账成功!");
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void methodB42() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 100);
updateById(xming);
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void methodB43() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 100);
updateById(xming);
int i = 1 / 0;
}
小王钱不变,小明两次收钱,只有一次成功收到钱加了100 A外事务,B1、B2内事务(平级关系); B2异常回滚,能影响A外事务回滚,但不影响B1事务提交
示例 47 嵌套内方法
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void methodA4() {
Account xwang = getById(1);
xwang.setMoney(xwang.getMoney() - 200);
updateById(xwang);
bccountService.methodB42();
System.out.println("转账成功!");
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void methodB42() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 100);
updateById(xming);
cccountService.methodC44();
int i = 1 / 0;
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void methodC44() {
Account xming = getById(3);
xming.setMoney(xming.getMoney() + 100);
updateById(xming);
}
A(祖父级) 包含 B(父级) 包含 C(子级),父级B异常 注意:C的SQL内容不能对小明进行修改,跟B的SQL内容对冲,会报错"Lock wait timeout exceeded; try restarting transaction",新增小马用户。 小王钱不变,小明钱也没变,小马钱多了100 父级B异常,自身事务回滚,子级C事务不被B影响正常提交,祖父级A事务被B影响也回滚
如果是子级C异常,那么3个事务都回滚 总结:
常见常用;
无论当前有没有事务,都始终自己创建新事务;
平级或嵌套事务时,异常事务回滚,父级和父级以上的外层事务也跟着回滚,但平级跟子级事务不影响正常提交。
Propagation.NOT_SUPPORTED
示例 51 外方法REQUIRED、内方法NOT_SUPPORTED,都有异常
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA5() {
Account xwang = getById(1);
xwang.setMoney(xwang.getMoney() - 200);
updateById(xwang);
bccountService.methodB52();
System.out.println("转账成功!");
int i = 1 / 0;
}
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED, rollbackFor = Exception.class)
public void methodB52() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 200);
updateById(xming);
int i = 1 / 0;
}
小王钱不变,小明钱多了200 外方法有异常,能回滚,有事务;内方法有异常,不能回滚,说明没有事务 总结:
少见少用;
不支持当前事务,无论有没有事务,都是以非事务方式运行
Propagation.NEVER
示例 61 外方法REQUIRED、内方法NEVER
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA6() {
Account xwang = getById(1);
xwang.setMoney(xwang.getMoney() - 200);
updateById(xwang);
bccountService.methodB62();
System.out.println("转账成功!");
}
@Override
@Transactional(propagation = Propagation.NEVER, rollbackFor = Exception.class)
public void methodB62() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 200);
updateById(xming);
}
外方法有事务,内方法被NEVER修饰,直接报错 总结:
少见少用;
不支持事务,只要有事务就报错,始终以非事务方式运行
Propagation.NESTED
示例 71 外方法REQUIRED、内方法NESTED,外方法异常
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA7() {
Account xwang = getById(1);
xwang.setMoney(xwang.getMoney() - 200);
updateById(xwang);
bccountService.methodB72();
System.out.println("转账成功!");
int i = 1 / 0;
}
@Override
@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void methodB72() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 200);
updateById(xming);
}
金额不变 外方法创建外事务,内方法不是加入到外事务中,而是在外事务里面再创建一个子事务,嵌套在外事务里; 此时外事务异常回滚,嵌套子事务也跟着回滚。
示例 72 外方法REQUIRED、内方法NESTED,内方法异常
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA7() {
Account xwang = getById(1);
xwang.setMoney(xwang.getMoney() - 200);
updateById(xwang);
bccountService.methodB72();
System.out.println("转账成功!");
}
@Override
@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void methodB72() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 200);
updateById(xming);
int i = 1 / 0;
}
金额不变 外方法创建外事务,内方法创建外事务的嵌套子事务,子事务有异常回滚了,也会导致外事务回滚 总结:
常见常用;
当前存在事务,则在该事务里面创建一个嵌套子事务运行;
当前没有事务,NESTED就等同于REQUIRED属性。
比较REQUIRED、REQUIRES_NEW和NESTED属性
定义serviceA.methodA()以PROPAGATION_REQUIRED修饰; 定义serviceB.methodB()以表格中三种方式修饰; methodA中调用methodB
异常状态 | PROPAGATION_REQUIRED | PROPAGATION_REQUIRES_NEW | PROPAGATION_NESTED |
---|
事务 | 同一事务 | 两个独立事务 | B事务嵌套在A事务中 | A正常B正常 | AB一起提交 | B先提交,A再提交 | AB一起提交 | A异常B正常 | AB一起回滚 | B先提交,A再回滚 | AB一起回滚 | A正常B异常 | AB一起回滚 | B先回滚;A捕获B异常A提交,A未捕获 | AB一起回滚 | A异常B异常 | AB一起回滚 | B先回滚,A再回滚 | AB一起回滚 |
|