1 事务(transaction)
事务是数据库的,并不是java的,一个事务中的所有操作,要么全部执行,要么全部不执行(回滚)
mysql的事务只针对DML语句,而不能操作DDL //例:①A表数据复制到B表②删除A表 如果①和②在同一个事务中,此时发生错误①(DML语句)能回滚②(DDL语句)不能回滚,导致的结果就是B表没有数据A表还被删除成功了
事务的4大特性:
(A) 原子性: 十五中的操作要么都生效,要么都不生效
(C) 一致性: 事务执行前后,数据库中定义的约束,业务规则等保持不变
(I) 隔离性: 并发访问数据库时,多个事物之间互不影响
(D) 持久性: 如果事务被成功提交,数据会持久保存在数据库
(原子性、一致性、持久性都是单事务; 隔离性是多事务并发)
脏读: 读取到另一个事务未提交的数据
不可重复读: 受其它事务update的干扰(期望是操作修改之前的数据)
虚读/幻读: 读取不到其它事务的insert/delete,但操作数据时提示"数据异常"等问题

2 事务传播行为
| 事务传播类型 | 传播特性 |
| REQUIRED | 当前线程有事务就加入当前线程,没有就新建事务 |
| SUPPORTS | 支持当前事务(当前线程有事务就加入当前事务,没有就以非事务执行) |
| MANDATORY | 当前线程有事务就加入当前事务,没有就抛异常 |
| REQUIRES_NEW | 不管当前线程有没有事务都新建一个事务,新事务与之前事务没有嵌套关系,之前的事务挂起 |
| NOT_SUPPORTED | 以非事务方式执行 |
| NEVER | 以非事务方式执行,如果当前存在事务就抛异常 |
| NESTED | (Spring提供的一个特殊变量)如果当前有事务,则创建一个嵌套事务;如果当前无事务,则新建事务 |
2.1 REQUIRED
只要将方法中的业务代码的异常捕获处理了,就不会回滚
@RequestMapping("/transactionalTest.do")
@Transactional
public Result aaa() {
guardNetDao.update(guardNetUpdateWrapper);
try {
int i = 1 / 0;
} catch (Exception e) {
System.out.println("aaa异常了");
}
transactionB.bbb();
transactionC.ccc();
}
@Transactional
public void bbb() {
alWindowDao.update(alWindowUpdateWrapper);
try {
int i = 1 / 0;
} catch (Exception e) {
System.out.println("bbb异常了");
}
}
@Transactional
public void ccc() {
alWindowModelsDao.update(alWindowModelsUpdateWrapper);
try {
int i = 1 / 0;
} catch (Exception e) {
System.out.println("ccc异常了");
}
}
都不回滚,事务正常执行
在方法外捕获异常,如果被捕获的方法有事务,回滚; 如果被捕获的方法没有事务,不会回滚
@RequestMapping("/transactionalTest.do")
@Transactional
public Result aaa() {
guardNetDao.update(guardNetUpdateWrapper);
transactionB.bbb();
try {
transactionC.ccc();
} catch (Exception e) {
System.out.println("ccc方法异常了");
}
}
@Transactional
public void bbb() {
alWindowDao.update(alWindowUpdateWrapper);
}
@Transactional
public void ccc() {
alWindowModelsDao.update(alWindowModelsUpdateWrapper);
int i = 1 / 0;
}
事务全部回滚
@RequestMapping("/transactionalTest.do")
@Transactional
public Result aaa() {
guardNetDao.update(guardNetUpdateWrapper);
transactionB.bbb();
try {
transactionC.ccc();
} catch (Exception e) {
System.out.println("ccc方法异常了");
}
}
@Transactional
public void bbb() {
alWindowDao.update(alWindowUpdateWrapper);
}
public void ccc() {
alWindowModelsDao.update(alWindowModelsUpdateWrapper);
int i = 1 / 0;
}
事务全部不回滚
@RequestMapping("/transactionalTest.do")
public Result aaa() {
guardNetDao.update(guardNetUpdateWrapper);
transactionB.bbb();
try {
transactionC.ccc();
} catch (Exception e) {
System.out.println("ccc方法异常了");
}
}
@Transactional
public void bbb() {
alWindowDao.update(alWindowUpdateWrapper);
}
@Transactional
public void ccc() {
alWindowModelsDao.update(alWindowModelsUpdateWrapper);
int i = 1 / 0;
}
aaa和bbb正常执行,ccc回滚
2.2 REQUIRES_NEW
内层事务结束,内层就提交了,不用等外层一起提交
外层报错回滚,不影响内层
内层报错回滚,外层try-catch了内层的异常,外层不会回滚
内层报错回滚,外层没有try-catch内层的异常,外层回滚
2.3 NESTED
内层嵌套事务结束,要等外层事务一起提交
外层回滚,内层嵌套事务也会滚
内层嵌套事务回滚,不影响外层事务 //内层回滚不影响外层是有前提的:在外层方法中将内层的异常捕获处理,否则内外都回滚
3 声明式事务
3.1 Spring声明式事务
对应声明式事务的是"编程式事务",通过代码的方式实现事务,事务的"开启,提交,回滚"等操作都由代码手动完成
底层原理是利用了AOP,声明式事务通过配置方式(XML配置,注解配置)管理数据库事务,核心特征是事务控制与业务逻辑解耦
(1) 添加依赖
添加spring-boot-starter依赖
(2) 配置事务管理器(注入数据源),开启Spring声明式事务的注解支持
在配置类添加@EnableTransactionManagement
(在SpringBoot中默认开启)
(3) 在类/方法(方法优先级更高)上添加@Transactional注解
/**
* readOnly=true 只读:只能查询,不能增删改
* timeout=10 超时(秒):回滚 默认-1(永不超时)
* 回滚策略:设置哪些异常回滚,哪些异常不回滚
* 设置回滚:rollbackFor={Exception.class,RuntimeException.class}
* rollbackForClassName={"全限定类名"}
* 设置不回滚:noRollbackFor={......}
* noRollbackForClassName={.....}
* isolation 事务隔离级别:解决"读的问题"
* =Isolation.DEFAULT使用数据库的默认隔离级别
* =Isolation.READ_UNCOMMITTED
* =Isolation.READ_COMMITTED (oracle默认,有"不可重复读")
* =Isolation.REPEATABLE_READ (mysql默认,有"虚读/幻读")
* =Isolation.SERIALIZABLE
* propagation 事务传播行为:事务之间的调用(例如:A调用B,那么要使用哪个的事务)
* Propagation.REQUIRED(默认)
* Propagation.SUPPORTS
* Propagation.MANDATORY
* Propagation.REQUIRES_NEW
* Propagation.NOT_SUPPORTED
* Propagation.NEVER
* Propagation.NESTED
*/
@Transactional(readOnly = false, timeout = 10, rollbackFor = RuntimeException.class, isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED)
public void aaa() {
userDao.aaa();
}
3.2 生效范围
被@Transactional标记的方法,在方法开始和结束,分别执行开启,提交,(异常)回滚事务,只要方法从开始到结束没有异常(即便有异常,但在方法中将异常捕获处理了),就不会回滚
标记方法的异常在上游方法中被捕获处理了,标记方法也会回滚,因为针对标记方法的AOP已经捕获到异常了,因此触发了回滚
3.3 内部调用@Transactional不生效
Spring通过AOP实现事务管理,当容器扫描到方法上有@Transactional时,会为目标生成一个代理类,此代理类的所有其他方法都是继承的原来的bean,当@Transactional标注的方法被调用时,实际上是由代理类来调用的,而在此代理类的此方法中的内部调用的同一个类其他方法都是调用的继承原bean的方法,因此当内部调用的方法即使被@Transaction标记了,也不会生效
1210

被折叠的 条评论
为什么被折叠?



