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/*Mapper.xml
type-aliases-package: com.yzm.transactional.entity
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
启用事务管理
@Configuration
@MapperScan("com.yzm.transactional.mapper")
@EnableTransactionManagement
public class MybatisPlusConfig {
}
目录结构
以下示例中,A称为外方法,B称为内方法
AB方法不能在同一个Service中,这跟spring的事务实现机制有关
比如A方法在AccountService类中,B方法在BccountService类中
@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;
}
@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);
}
@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);
int i = 1 / 0;
}
从上面三个示例可以看出:
REQUIRED修饰的内外方法是在同一个事务中的,只要其中一个方法抛出异常,整个事务就会回滚。
@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);
int i = 1 / 0;
}
结果跟 @示例 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("转账成功!");
// 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);
int i = 1 / 0;
}
外方法以非事务方式运行的,小王钱少了200
内方法有异常,小明钱没变,说明事务回滚了
@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("转账成功!");
// int i = 1 / 0;
}
@Override
// @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB12() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 100);
updateById(xming);
// int i = 1 / 0;
}
@Override
// @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB13() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 100);
updateById(xming);
int i = 1 / 0;
}
内方法B13有异常,金额不变,说明3个方法都在同一事物中,有异常导致整个事务回滚
如果是嵌套内方法也是一样的,都在同一事务中。
总结:
默认,最常用的;
支持事务,当前存在事务则加入,不存在就创建事务;
最外层方法必须加事务注解,里层方法可加可不加(不加注解的里层方法会被外层事务包裹);
外层方法和里层包裹的方法始终在同一事务中,只要任意一个方法抛出异常(未捕获),整个事务就会回滚。
@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("转账成功!");
// int i = 1 / 0;
}
@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;
}
外方法创建事务,内方法(支持事务)加入该事务,使得内外方法处于同一事务中,内方法抛出异常,导致整个事务回滚,金额不变。
@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("转账成功!");
// int i = 1 / 0;
}
@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,说明内方法没有事务,也是以非事务方式运行
@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.SUPPORTS, rollbackFor = Exception.class)
public void methodB22() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 200);
updateById(xming);
int i = 1 / 0;
}
内方法有异常,未显示事务提交,但是金额都变了,说明是以非事务方式运行
@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,都是以非事务方式运行,有异常不回滚。
@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("转账成功!");
// int i = 1 / 0;
}
@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 一样
外方法创建事务,内方法(支持事务)加入外方法事务,同一事务,事务回滚,金额不变。
@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("转账成功!");
// int i = 1 / 0;
}
@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;
}
总结:
少见少用;
支持当前事务,有事务则加入事务,没有事务就会抛出异常,显示错误信息。
单独调用或者最外层方法是MANDATORY,就直接报错
@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);
// int i = 1 / 0;
}
小王钱不变小明钱多了200
外方法创建外事务,内方法创建内事务,外方法有异常,只会回滚外事务,内事务正常提交。
@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);
int i = 1 / 0;
}
金额都不变
外方法创建外事务,无异常;
内方法创建内事务,有异常导致内事务回滚,同时也让外事务回滚
@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);
// int i = 1 / 0;
}
金额都不变
外方法创建事务后,内方法就不再创建事务而是加入到外方法的事务中,同一事务,外异常,就回滚
改成内异常也是整个事务回滚。
@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);
// int i = 1 / 0;
}
小王钱不变,小明钱凭空多了200
跟 示例 41 是一样的,内外方法都有自己的事务,外方法有异常外事务回滚,内事务不影响
@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);
int i = 1 / 0;
}
金额都不变
跟 示例 42 是一样的,内外方法都有自己的事务,内方法有异常导致内事务回滚,外事务也跟着回滚
@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("转账成功!");
// int i = 1 / 0;
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void methodB42() {
Account xming = getById(2);
xming.setMoney(xming.getMoney() + 100);
updateById(xming);
// int i = 1 / 0;
}
@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事务提交
@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() + 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);
// int i = 1 / 0;
}
A(祖父级) 包含 B(父级) 包含 C(子级),父级B异常
注意:C的SQL内容不能对小明进行修改,跟B的SQL内容对冲,会报错"Lock wait timeout exceeded; try restarting transaction",新增小马用户。
小王钱不变,小明钱也没变,小马钱多了100
父级B异常,自身事务回滚,子级C事务不被B影响正常提交,祖父级A事务被B影响也回滚
如果是子级C异常,那么3个事务都回滚
总结:
常见常用;
无论当前有没有事务,都始终自己创建新事务;
平级或嵌套事务时,异常事务回滚,父级和父级以上的外层事务也跟着回滚,但平级跟子级事务不影响正常提交。
@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
外方法有异常,能回滚,有事务;内方法有异常,不能回滚,说明没有事务
总结:
少见少用;
不支持当前事务,无论有没有事务,都是以非事务方式运行
@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);
}
少见少用;
不支持事务,只要有事务就报错,始终以非事务方式运行
@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);
// int i = 1 / 0;
}
金额不变
外方法创建外事务,内方法不是加入到外事务中,而是在外事务里面再创建一个子事务,嵌套在外事务里;
此时外事务异常回滚,嵌套子事务也跟着回滚。
@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);
int i = 1 / 0;
}
金额不变
外方法创建外事务,内方法创建外事务的嵌套子事务,子事务有异常回滚了,也会导致外事务回滚
总结:
常见常用;
当前存在事务,则在该事务里面创建一个嵌套子事务运行;
当前没有事务,NESTED就等同于REQUIRED属性。
定义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一起回滚 |