2021-03-15

JAVA事务的隔离级别与事务的传播行为

java事务特性

说到事务,就不得不提它的特性ACID
四大特性:原子性,一致性,隔离性,持久性
1). 原子性(atmoicity):组成事务处理的语句形成一个逻辑单元,不能只执行其中的一部分。
2). 一致性(consistency):事务执行时数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定
3). 隔离性(isolation):一个事务不会影响到另一个事务。
4). 持久性(durability):事务处理的结果能永久保存

隔离级别

1)为了应对多线程并发读取数据时出现的问题,事务由了“隔离级别”特性,多线程并发读取数据一般会引起以下三个问题:
1. 脏读
2. 不可重复度
3. 幻读
下面进行简要介绍:
1. 脏读:一个事务读取到另一个事务未提交的数据。
2. 不可重复度:一个事务重新读取前面已经读取的数据,发现第二次读取的数据于第一次读取的数据由区别,就是被修改了。
3. 幻读:一个事务重新执行一个查询,返回一套符合条件的数据,发现这些数据因为其他最近提交的事务而发生改变。
2). 为了解决以上读取数据的问题,java事务提供了四种隔离级别
1. 读未提交
2. 读已提交
3. 可重复读
4. 可串性化
3). 四种隔离级别与上面3个问题的对应关系如下
V:可能出现,X:不会出现

事务隔离级别脏读不可重复读幻读
读未提交VVV
读已提交XVV
可重复读XXV
可串性化XXX
  1. 读未提交:顾名思义,就是一个事务可以读取到另一个事务未提交的数据,该隔离级别会导致脏读。
    事例:公司发工资,A职员的工资时3.6w/月,但在发工资是由于财务读不小心按称来3.6w/月,该钱已经发到A职员的卡上,但是事务没有提交。这是A职员查询发现由3.9w,必平常多了3k,以为涨工资了,但是财务及时发现不对,马上对事务进行回,提交了事务,将工资改成3.6w再提交。所有这就造成了脏读
    对脏读的解决办法由,将事务级别改为读已提交。
  2. 读已提交:就是一个事务要等另一个事务提交后才能读取到数据,该隔离级别会导致不可重复读。
    事例:A职员知道自己工资卡上由3.6w,准备去消费体验生活,当他要买单时,收费系统事先检查但它工资卡上还有3.6w,就在这个时候,A职员的老婆将A职员的工资全部取出来拿去当家用了,并提交了。当收费系统准备扣款时发现已经没钱了(第二次检查金额等待老婆转出金额事务提交完)。
    对不可重复读的解决方案,将事务隔离级别修改成可重复读。
  3. 可重复读:就是在开始读取数据时,开启事务,还未提交,不再允许就行修改操作。该隔离级别会造成幻读
    事例:A职员去体验生活,当天消费了2k,然后他老婆去查看他今天的消费,看到的确消费了2k。就在这是A职员有消费了2k,即再这个时候有insert了一条消费记录,并提交事务了,当他老婆打印A职员消费记录时发现花费了4k,A职员老婆以为出现幻觉,所有这就是幻读。
  4. 可串性化:是最高级的事务隔离级别,能解决上面所描述的三个问题,但时这个隔离级别效率低,比较耗数据库的性能,所有一般部适用

值得一提的是:大多数的数据库都是默认读已提交,比例Orcal。Mysql则默认的是可重复读。

Spring的事务传播机制

Spring对事物的控制是使用Aop切面来实现的,我们不用关系事务的开始,提交,回滚,只需要再方法上加上@Transactional注解,
场景一:serviceA方法调用serviceB方法,但是两个方法都有事务,这个时候如果serviceB事务出现异常,是让serviceB方法提交,还是两个一起回滚。
场景二:serviceA方法调用serviceB方法,但是只有serviceA方法加了事务,再调用serviceB方法时,是否也会把serviceB加到serviceA中,如果serviceB出现异常,是否会回滚serviceA呢?
场景三:serviceA方法调用serviceB方法,两种都有事务,serviceB已经执行正常,但serviceA出现异常,是否需要回滚serviceB吗?

在spring-tx工程下的TransactionDefinition接口中定义了7中事务传播行为,它们规定了事务方法及事务方法嵌套调用时的事务传播方式。

  1. REQUIEED
    如果当前没有事务,就新建一个事务,如果已经存在一个事务,则加入到该事务中,而这时Spring的事务默认选择
    比例
@Transactional
methodA{
	methodB();
}
@Transactional
methodB();
  1. SUPPORTS
    支持当前事务,如果当前没有事务,就以非事务执行
@Transactional
methodA{
	methodB();
}
@Transactional(propagation = propagation.SUPPORT)
methodB();

如果B单独调用,则B以非事务的方式执行,如果在A中调用B,因为A有事务,所有B将加入到A事务中
3. MANDATORY
使用当前的事务,如果当前没有事务,就会抛出异常

@Transactional
methodA{
	methodB();
}
@Transactional(propagation = propagation.MANDATORY)
methodB();

如果单独执行B,则会抛出异illegalTranactionStateException异常,如果在A方法中调用B方法,则B加入到A事务中
4. REQUIRES_NEW

@Transactional
methodA{
	methodB();
}
@Transactional(propagation = propagation.REQUIRES_NEW)
methodB();

调用B方法,总会创建一个新的事务,如果在A方法中调用B方法,则A方法的事务会挂起,执行B方法事务,如果A事务失败回滚,并不会导致B事务回滚,也就是事务方法B的成功失败并不会依赖于A方法

  1. NOT_SUPPORTED
    以非事务方式执行操作,如果当前存在事务,就吧当前事务挂起
@Transactional
methodA{
	methodB();
}
@Transactional(propagation = propagation.NOT_SUPPORED)
methodB();
  1. NEVER
    以非事务方式执行,如果当前存在事务,就会抛出异常
@Transactional
methodA{
	methodB();
}
@Transactional(propagation = propagation.NEVER)
methodB();

如果调用B则以非事务的方式执行,如果在A中调用B则会抛出异常
7. NESTED
如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则执行创建一个事务。

@Transactional
methodA{
	methodB();
}
@Transactional(propagation = propagation.NESTED)
methodB();

如果调用B则新建一个事务,如果在A方法中调用B,则就是传说中的嵌套事务,可以理解未主事务和子事务的关系,主事务可以控制子事务,级主事务的提交和回滚,子事务也对应提交和回滚,而子事务报错时可以回滚到savepoin,而主事务继续执行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值