考虑事务使用地方:
1)、哪些东西是一定要回滚的、哪些即使出错了不必要回滚的。
商品的核心信息(基本数据、sku---销售属性)保存的时候,不要受到别的无关信息的影响。
无关信息出问题,核心信息也不用回滚的。
2)、事务的传播行为;propagation:当前方法的事务[是否要和别人公用一个事务]如何传播下去(里面的方法如果用事务,是否和他公用一个事务)
REQUIRED:(必须):
如果以前有事务,就和之前的事务公用一个事务,没有就创建一个事务;
REQUIRES_NEW(总是用新的事务):
创建一个新的事务,如果以前有事务,暂停前面的事务。
SUPPORTS(支持):
之前有事务,就以事务的方式运行,没有事务也可以;
MANDATORY(强制):没事务就报错
一定要有事务,如果没事务就报错
NOT_SUPPORTED(不支持):
不支持在事务内运行,如果已经有事务了,就挂起当前存在的事务
NEVER(从不使用):
不支持在事务内运行,如果已经有事务了,抛异常
NESTED:
开启一个子事务(MySQL不支持),需要支持还原点功能的数据库才行;
形象例子说明以上传播行为:
一家人带着老王去旅游;
一家人:开自己的车还是坐老王的车
Required:坐老王车
Requires_new:一定得开车,开新的
SUPPORTS:用车,有车就用,没车走路;
MANDATORY:用车,没车就骂街。。。
NOT_SUPPORTED:不支持用车。有车放那不用
NEVER:从不用车,有车抛异常
例题感受实际用法
外事务{
A();//事务.Required:如果执行到最后一句10/0,出错,跟着回滚
b();//事务.Requires_new:如果执行到最后一句10/0,出错,不回滚
int i = 10/0;
}
外事务{
调用A()方法;Required; A
调用B()方法;Requires_new B
调用C()方法;Required; C
//try{
//C();Required; C
//}catch(Exception e){
//c出异常?
//}
调用D()方法;Requires_new; D
//给数据库存 --外
}
在上面的例子为前提判断下面场景,哪些会回滚
场景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只要感觉到异常就一定回滚。和外事务是什么传播行为无关。
传播行为总是来定义,当一个事务存在的时候,他内部的事务该怎么执行。
事务的问题:
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {
@Transactional(rollbackFor = Exception.class)
@Override
public void saveProduct(PmsProductParam productParam) {
//1.保存商品表的基本信息,返回商品id,供下面其他信息保存关联使用
saveBaseInfo(productParam);
//2.保存sku库存信息
saveSkuStock(productParam);
//3.保存满减价格
saveFullReduction(productParam);
//4.保存会员价格
saveMemberPrice(productParam);
//5.保存商品参数及自定义规格属性
saveProductAttributeValue(productParam);
}
//声明事务传播性为REQUIRES_NEW,调用这个方法的下面其他语句,就算出错,此方法也不会回滚
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
public void saveBaseInfo(PmsProductParam productParam) {
//执行方法。。。。
}
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
public void saveSkuStock(PmsProductParam productParam) {
//执行方法。。。。
}
@Transactional(rollbackFor = Exception.class)
public void saveFullReduction(PmsProductParam productParam) {
int i=5/0;
//执行方法。。。。
}
@Transactional(rollbackFor = Exception.class)
public void saveMemberPrice(PmsProductParam productParam) {
//执行方法。。。。
}
@Transactional(rollbackFor = Exception.class)
public void saveProductAttributeValue(PmsProductParam productParam) {
//执行方法。。。。
}
}
- 在上面saveProduct方法中,里面有一堆方法,用来保存商品,折扣,满减信息....到数据库。
- 然后每个方法加上事务注解,前两个为REQUIRES_NEW,后两个方法为默认REQUIRES,我们这样设计是为了保证不会全部回滚,因为有些重要的信息还是要保存的,省的在前端又填一遍。
- 我们在第三个方法saveFullReduction加入了一句错误语句,等执行到他的时候,看起来像是我们期待的样子,前两个不回滚,数据库有数据。
- 但实际上却不会生效,因为注解事务实际上是通过AOP代理实现的,saveProduct方法里调用的其他方法只是把其他方法里的语句粘贴过来而已,并不会有事务,所以这样就是一个出错,整体回滚。
解决办法:使用AopContext.currentProxy() 解决Spring注解失效,这个方法可以调用到此类的代理类,通过调用代理类的各个方法,这样就都有事务了,会生效了。
1.第一步将saveProduct()方法改成这样
public void saveProduct(PmsProductParam productParam) {
ProductServiceImpl proxy = (ProductServiceImpl) AopContext.currentProxy();
//1.保存商品表的基本信息,返回商品id,供下面其他信息保存关联使用
proxy.saveBaseInfo(productParam);
//2.保存sku库存信息
proxy.saveSkuStock(productParam);
//3.保存满减价格
proxy.saveFullReduction(productParam);
//4.保存会员价格
proxy.saveMemberPrice(productParam);
//5.保存商品参数及自定义规格属性
proxy.saveProductAttributeValue(productParam);
}
2.开启暴露代理类注解
@EnableAspectJAutoProxy(exposeProxy = true)
@SpringBootApplication
public class GmallPmsApplication {
public static void main(String[] args) {
SpringApplication.run(GmallPmsApplication.class, args);
}
}
事务传播行为:
隔离级别:解决读写加锁问题的(数据底层的方案)。 可重复读(快照);
运行时异常(不受检查异常)
ArithmeticException ......
编译时异常(受检异常)
FileNotFound;1)要么throw要么try- catch
运行的异常默认是一定回滚
编译时异常默认是不回滚的;
rollbackFor:指定哪些异常一定回滚的。