一个业务有一组操作,要么都成功,要么都失败
A 原子性:一组操作,要么都成功,要么都失败
C 一致性 :事务的前后要保证事务的一致性
I 隔离性 :很重要,两个事务之间的数据不能被相互影响
D 持久性
事务分两类:
编程式事务:就像我们之前写的JDBC的编写事务的方法
connection.setAutoCommit(true);
connection.beginTransaction();
connection.commit();
connection.rollback();
以上是编程式事务
我们今天学的就是声明式的事务,基于AOP的方式
事务管理器:
Understanding the Spring Framework Transaction Abstraction :: Spring Frameworkhttps://docs.spring.io/spring-framework/reference/6.1-SNAPSHOT/data-access/transaction/strategies.html
无论是JDBC还是Mybatis还是Hibernate都需要一个事务管理器,事务管理器最终还是针对Connection进行事务 管理,所以要把数据源交给事务管理器
之后添加事务的驱动
事务的实现原理?
事务底层是用AOP实现的,加上@Transactional注解之后,会依据动态代理创建一个子类,进而会将这个方法作为切入点,切面就是之前编程式事务的内容,在要执行的方法前后加上事务的逻辑commit和rollback,涉及到了跨方法,将conn放入ThreadLocal中
可以标记在方法上,也可以标记在类上面(写在方法上事务控制逻辑粒度更细)
一般@Transactional写在业务逻辑方法上面,
@Transactional(isolation =xxx ,propagation = xxx)
事务注解中有两个属性:重要
@Transactional(isolation = Isolation.REPEATABLE_READ )
a 读未提交:read uncommitted 事务1读到了事务2的未提交的数据,如果事务2将这个操作做回滚了,那么事务1读到的就是脏数据
b 读已提交:read committed 事务1在没有提交之前读到了事务2已经提交的数据,造成了这个事务1还没有结束,两次读取的结果不一样,进而形成了不可重复读的问题
c 可重复读 repeatable read (其实是上了一个行锁)事务1开始了还没结束 ,读取表数据,这时候事务2修改了表数据并且已经提交,那么事务1在这个事务中是查不到修改的数据的,但是事务1能查询到事务2已经提交的增加的数据,这就是幻影读。
d 事务串行化 表锁 最安全,性能也最差
在mysql中查看事务的隔离级别:SELECT @@transaction_isolation;
对应的结果:REPEATABLE-READ(Mysql的默认级别是可重复读)
设置本次会话的事务隔离级别,四种:
//设置read uncommitted级别:
set session transaction isolation level read uncommitted;
//设置read committed级别:
set session transaction isolation level read committed;
//设置repeatable read级别:
set session transaction isolation level repeatable read;
//设置serializable级别:
set session transaction isolation level serializable;
isolation\出现的问题 |
脏读(结果覆盖) |
不可重复读 |
幻读 |
读未提交 |
出现 |
出现 |
出现 |
读已提交 |
不出现 |
出现 |
出现 |
可重复读 |
不出现 |
不出现 |
出现 |
事务串行化 |
不出现 |
不出现 |
不出现 |
2.propagation:传播属性
一般是在Service业务逻辑方法中,一个含有事务的方法调用了另外一个含有事务的方法时候会出现事务的传播
REQUIRED,SUPPORTS和REQUIRES_NEW这3中是比较常用的,是重点
注意:当有挂起外部事务的情况的时候,调用方法和被调用的方法不能在一个类中
propagation |
外部方法不存在事务 |
外部方法存在事务 |
对应注解 |
REQUIRED |
开启新的事务 |
融入到外部事务 |
@Transactional(propagation = Propagation.REQUIRED) 适合增删改查 |
SUPPORTS |
不开启新的事务 |
融入到外部事务 |
@Transactional(propagation = Propagation.SUPPORTS) 适合查询方法 |
REQUIRES_NEW |
开启新的事务 |
挂起外部事务 |
@Transactional(propagation = Propagation.REQUIRES_NEW) 适合业务方法中的业务和外部的方法中的业务关联不大的情况,比如日志 |
NOT_SUPPORTED |
不开启新的事务 |
挂起外部事务 |
@Transactional(propagation = Propagation.NOT_SUPPORTED) 就是这么酷,不想自己有事务,外部有事务也别影响我 |
NEVER |
不开启新的事务 |
抛出异常 |
@Transactional(propagation = Propagation.NEVER) 就是别想有事务 |
MANDATORY |
抛出异常 |
融合到外部事务 |
@Transactional(propagation = Propagation.MANDATORY) 任性,我就是要事务 |
3.@Transactional(timeout = 2)超时时长,单位是秒
// 或者
//以上二选一即可