事务的特性
一个事务必须要确保它的原子性,一致性,隔离性,持久性
- 原子性:一个事务,相当于客户下单需要新建一张订单然后保存到数据库中,新建订单就是一个事务,要么整个订单正确保存完成,要么完全不保存。
- 一致性:一旦这张订单完成后,无论是否成功,整个业务的流程对于这张订单的状态都是一致的,不会一部分下单成功,一部分下单失败。
- 隔离性:当很多的用户都要下单的时候,每个用户的订单之间要互相不干扰,隔离开来。
- 持久性:订单完成后,就把订单保存到持久化存储器中(数据库),就不会因为系统错误导致订单发生错误。
开启Spring事务有两种方式
编程式事务管理和声明式事务管理
- 编程式事务管理:可以用TransactionTemplate工具类,它可以用在代码块中,控制事务的范围,如图1
- 声明式事务管理:声明式事务管理是建立在springAOP上的,在方法前和方法后进行拦截,可以通过注解的方式将开启一个事务,如图2
图一:↓
//先声明transactionTemplate
@Autowired
private TransactionTemplate transactionTemplate;
//方法中的代码块,TransactionTemplate工具类可以控制细粒度事务
transactionTemplate.execute(new TransactionCallback() {
@Override
public Object doInTransaction(TransactionStatus transactionStatus) {
//执行订单保存,更新,删除等操作如:xxx.updateByExample(这是调用Mybatis的dao层的接口,Mybatis会根据同名的XML中的配置信息创建一个代理类,动态代理,然后执行sql保存信息,完成事务)
return Boolean.TRUE;
}
});
图2:↓
//订单的service实现类
@Service
public class OrderServiceImpl extends BaseServiceImpl<OrderMapper, Order> implements OrderService{
//以下是增加订单的方法,注解对于整个add方法进行事务管理,括号内就是如果有Exception返回,则对整个方法内的流程回滚,不进行任何修改
//propagation 为事务传播机制(xxx可以按自身业务需求修改事务传播机制)
@Transactional(rollbackFor = Exception.class,propagation = Propagation.xxx)
public boolean add(Order order, SySUser user) {
//这里面是写的业务流程如,set订单号,订单价格,订单状态等
//del()为内层事务
del();
return true;
}
//删除订单
@Transactional(rollbackFor = Exception.class)
public boolean del(Order order, SySUser user) {
//这里面是写的业务流程如
return true;
}
}
事务的传播机制
事务的传播机制指的是在事务嵌套的场景中,比如一个事务方法里调用另一个事务方法(如:上面的add()方法是个事务,add方法里调用del()方法)。那么这两个方法是如何去提交呢,是分别单独提交还是一起提交,这就是事务传播机制来确定的。常用的事务传播机制如下:
- PROPAGATION_REQUIRED: 这个是默认的事务传播机制,例子如上图,如果del()方法定义传播机制为PROPAGATION_REQUIRED,add()方法也开启了事务,然后add()方法中调用del()方法,那么add()方法为外层事务,del()方法为内层事务,那么add()方法就会启动事务,del()方法查看到自己在add()方法的事务中,就不会开启事务,这个时候只有一个add()方法的事务,两个方法共同用一个事务,无论哪个方法中抛出异常,都会导致两个方法一起回滚,或者一起成功。
- PROPAGATION_REQUES_NEW:还是例子如上图,如果del()方法定义传播机制为PROPAGATION_REQUIRED_NEW,add()方法也开启了事务,会先把add()的事务挂起(停止,先放一边),执行del()的事务,完成del()的事务后再执行add()的事务,如果add()也就是外层事务中有抛出异常,那么仅仅是add()方法进行回滚,不会影响到del()的事务,如果del()方法也就是内层事务抛出异常,那么add()和del()方法都要进行回滚,总的来说就是PROPAGATION_REQUIRED_NEW传播机制就是会开启一个新的事务,外层事务不会对内层事务产生影响,但是内层事务的异常会影响外层事务的回滚。
- PROPAGATION_SUPPORT:例子如上图,如果add()方法开启事务,del()方法开启事务为PROPAGATION_SUPPORT,那么del()的事务就会加入add()的事务,如果add()没有开启事务,del()方法也用非事务方法执行,这里就有一个疑问,PROPAGATION_SUPPORT这个事务传播机制不就和不加 @Transactional一样吗?这个问题可以去搜一下如https://www.cnblogs.com/zhangliang88/p/12803356.html
- PROPAGATION_NOT_SUPPORT:这个事务传播机制就如同名字一样,不支持事务,例子如上图,如果add()开启了事务,然后del()的事务传播机制为PROPAGATION_NOT_SUPPORT,那么在执行del()方法的时候,会吧add()挂起放一边,然后完成del()的业务流程,再回到add()中执行事务,无论add()中事务是成功失败,都不会影响del()的结果。
- PROPAGATION_NEVER:例子如上图,如果del()方法的事务传播机制为PROPAGATION_NEVER,那么如果它发现它的外层有事务开启,那么就会直接抛出错误。
- PROPAGATION_MANDATORY:这个事务传播机制与PROPAGATION_NEVER相反,如果它发现它的外层没有事务开启,那么就会直接抛出错误。
- PROPAGATION_NESTED:这个事务传播机制比较特殊,它是一个真正的嵌套事务,例子如上图,如果del()开启PROPAGATION_NESTED事务传播机制,而add()如果没有开启事务,则del()的传播机制按PROPAGATION_REQUIRED处理,如果add()开启了事务,那么del()就是add()的事务的子事务,和PROPAGATION_REQUES_NEW事务传播机制(这个事务传播机制,内层事务会开启新的事务)产生了区别,PROPAGATION_NESTED事务传播机制则是依赖于外层事务,外层事务提交,子事务才会提交,回滚也是一样的。可以参考https://blog.csdn.net/yanxin1213/article/details/100582643
事务的隔离级别
事务隔离级别,就是根据不同的业务需求和环境,规定不同事务之间的影响程度,在并发的环境下,当多个事务需要对同一个数据进行操作时,不同的级别会导致不同的影响,完全的隔离就相当于一个事务一个事务的去操作数据,这会影响程序性能,而不完全隔离又会造成脏读,不可重复度和幻读。
- 脏读:脏读发生在一个事务读取了被另一个事务改写但尚未提交的数据时。如果这些改变在稍后被回滚了,那么第一个事务读取的数据就会是无效的。
- 不可重复读:不可重复读发生在一个事务执行相同的查询两次或两次以上,但每次查询结果都不相同时。这通常是由于另一个并发事务在两次查询之间更新了数据。
- 幻读:幻读和不可重复读相似。当一个事务(T1)需要写入一条数据,写入前判断id是否存在,不存在才写入,但是当T1差询到id不存在,但还没写入的时候,另一个并发事务(T2)插入了一条记录,id就是T1判断的id的值,那么T1插入数据就会报错相同的id冲突看了,幻读就发生了。
Spring事务隔离级别有四种:
- ISOLATION_DEFAULT:使用后端数据库默认的隔离级别。
- ISOLATION_READ_UNCOMMITTED: 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据,这种隔离级别会产生脏读,不可重复读和幻读。
- ISOLATION_READ_COMMITTED:允许从已经提交的并发事务读取。可防止脏读,但幻读和不可重复读仍可能会发生。
- ISOLATION_REPEATABLE_READ:对相同字段的多次读取的结果是一致的,除非数据被当前事务本身改变。可防止脏读和不可重复读,但幻读仍可能发生。
- ISOLATION_SERIALIZABLE:完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。
以上内容为自己查,然后按自己的理解写的