事务传播行为
事务的传播行为;propagation:当前方法的事务[是否要和别人公用一个事务]如何传播下去(里面的方法如果用事务,是否和他公用一个事务)
Propagation propagation() default Propagation.REQUIRED;
代码语言:javascript复制 REQUIRED:(必须):
Support a current transaction, create a new one if none exists.
如果以前有事务,就和之前的事务公用一个事务,没有就创建一个事务;
REQUIRES_NEW(总是用新的事务):
Create a new transaction, and suspend the current transaction if one exists.
创建一个新的事务,如果以前有事务,暂停前面的事务。
SUPPORTS(支持):
Support a current transaction, execute non-transactionally if none exists.
之前有事务,就以事务的方式运行,没有事务也可以;
MANDATORY(强制):没事务就报错
Support a current transaction, throw an exception if none exists
一定要有事务,如果没事务就报错
NOT_SUPPORTED(不支持):
Execute non-transactionally, suspend the current transaction if one exists
不支持在事务内运行,如果已经有事务了,就挂起当前存在的事务
NEVER(从不使用):
Execute non-transactionally, throw an exception if a transaction exists.
不支持在事务内运行,如果已经有事务了,抛异常
NESTED:
Execute within a nested transaction if a current transaction exists,
开启一个子事务(MySQL不支持),需要支持还原点功能的数据库才行;
举例子: 一家人带着老王去旅游;
代码语言:javascript复制 一家人:开自己的车还是坐老王的车
Required:坐老王车
Requires_new:一定得开车,开新的
SUPPORTS:用车,有车就用,没车走路;
MANDATORY:用车,没车就骂街。。。
NOT_SUPPORTED:不支持用车。有车放那不用
NEVER:从不用车,有车抛异常
其实平常用到的也就Required Requires_new
例子分析:
代码语言:javascript复制Required_new
外事务{
A();Required; A
B();Requires_new B
//try{
C();Required; C
//}catch(Exception e){
//c出异常?
//}
D();Requires_new; D
//给数据库存 --外事务的(就是本方法的)
// int i = 10/0;
}
- 场景1: A方法出现了异常;由于异常机制导致代码停止,下面无法执行,数据库什么都没有
- 场景2: C方法出现异常;A回滚,B成功,C回滚,D无法执行,外无法执行
- 场景3: 外成了后,int i = 10/0; B,D成功。A,C,外都执行了但是必须回滚
- 场景4: D炸;抛异常。外事务感知到异常。A,C回滚,外执行不到,D自己回滚,B成功
- 场景5: C如果用try-catch执行;C出了异常回滚,由于异常被捕获,外事务没有感知异常。A,B,D都成,C自己回滚
总结: 对这段代码而言 传播行为过程中,只要Requires_new被执行过就一定成功,不管后面出不出问题。异常机制还是一样的,出现异常代码以后不执行。 Required只要感觉到异常就一定回滚。和外事务是什么传播行为无关。
传播行为总是来定义,当一个事务存在的时候,他内部的事务该怎么执行。
实例用法: 下面这个方法里的每一个save方法都用重新开启一个事务的
代码语言:javascript复制@Transactional(propagation = Propagation.REQUIRES_NEW)
代码语言:javascript复制 @Transactional(propagation = Propagation.REQUIRED)
@Override
public void saveProduct(PmsProductParam productParam) {
ProductServiceImpl proxy = (ProductServiceImpl) AopContext.currentProxy();
//1)、pms_product:保存商品基本信息
proxy.saveBaseInfo(productParam);
//5)、pms_sku_stock:sku_库存表
proxy.saveSkuStock(productParam);
/**
* 2如果炸了 3 4 是执行不到的 如果想互不影响 可以每个try-catch来互不影响
*/
//2)、pms_product_attribute_value:保存这个商品对应的所有属性的值
proxy.saveProductAttributeValue(productParam);
//3)、pms_product_full_reduction:保存商品的满减信息
proxy.saveFullReduction(productParam);
//4)、pms_product_ladder:满减表
proxy.saveProductLadder(productParam);
}
那么这段代码为什么要用AopContext.currentProxy()
获取当前代理对象呢?
spring的事务是aop动态代理做的 我们想用事务 必须获取代理对象来调用方法 通过 对象.方法()才能加上事务。
例如:
A{
A(){
B(); //1,2,3
C(); //4,5,6
D(); //7,8,9
}
}
A类调用自身的B C D 三个方法 其实就是这样
代码语言:javascript复制 A(){
//1,2,3,4,5,6,7,8,9
//
}
自己类调用自己类里面的方法,就是一个复制粘贴。归根到底,只是给 controller{ serviceProxy.a(); } 只有一个事务 而我们使用
代码语言:javascript复制 A{
A(){
hahaService.B();
hahaService.C();
hahaService.D();
}
}
是可以在A类的A方法内部开启事务的。
事务的问题: Service自己调用自己的方法,无法加上真正的自己内部调整的各个事务 因此我们这样解决: 要是能拿到ioc容器,从容器中再把我们的组件获取一下,用对象调方法。
事务的最终解决方案;
普通加事务。导入jdbc-starter,@EnableTransactionManagement,加@Transactional
方法自己调自己类里面的加不上事务。
- 导入aop包,开启代理对象的相关功能
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 获取到当前类真正的代理对象,去调方法即可 1)、@EnableAspectJAutoProxy(exposeProxy = true):暴露代理对象 2)、获取代理对象;
隔离级别
隔离级别:通过解决读写加锁问题的(数据底层的方案)。 mysql默认可重复读(快照);
读未提交:改数据时候不加锁 别人可以读 读已提交:改数据时候加锁 数据改完才能读 可重复读:只要这个线程没释放完 读的都是之前的数据 串行化:
异常回滚策略
异常回滚策略 异常: 运行时异常(不受检查异常) ArithmeticException … 编译时异常(受检异常) FileNotFound;编译时异常要么throw要么try- catch
事务运行的异常默认是一定回滚 编译时异常默认是不回滚的; 可以通过rollbackFor:指定哪些异常一定回滚的。