- 事务ACID原则
- 原子性:原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
- 一致性:事务前后数据的完整性必须保持一致。
- 隔离性:事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
- 持久性: 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
- 传播行为
-
简介(需要熟悉呦~~~)
行为名称 作用 REQUIRED 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。(default) SUPPORTS 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。但是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有少许不同。 MANDATORY 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。(必须运行在事务内) REQUIRES_NEW 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。(新开事务,如果已经有一个事务,那么就是有两个事务) NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务 NEVER 总是非事务地执行,如果存在一个活动事务,则抛出异常(必须事务外) NESTED 如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行 -
代码
- server1
import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; @Service public class Server1 { @Resource private DeptMapper deptMapper; @Resource private Server2 server2; /* @Transactional( )*/ public void update () { server2.update(); Dept dept = new Dept(); dept.setDeptno(43); dept.setLoc("中国-1"); dept.setDname("事业部-1"); int i = deptMapper.updateByPrimaryKey(dept); int b = 1 / 0; System.out.println(i); } }
- Server2
@Service public class Server2 { @Resource private DeptMapper deptMapper; @Transactional public void update () { Dept dept = new Dept(); dept.setDeptno(46); dept.setLoc("中国-2"); dept.setDname("事业部-2"); int i = deptMapper.updateByPrimaryKey(dept); } }
- 测试类
@SpringBootTest(classes = TranscationdemoApplicationTests.class) @ComponentScan("com.zh") @EnableAutoConfiguration @MapperScan("com.zh.transcationdemo.dao") class TranscationdemoApplicationTests { @Resource private SelectThread selectThread; @Resource private UpdateThread updateThread; @Resource private Server1 server1; /** * 测试 事务的传播行为 和 事务的隔离性 */ @Test public void testNone() { server1.update(); } }
- server1
-
场景
- REQUIRED [如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。(default)]
- server1 update 方法使用@Transactional,server2 update 方法不使用 @Transactional
- 则server1和 server2的数据都不会更新,此时默认使用 REQUIRED
- 数据都会回滚
- server1 update 方法使用@Transactional,server2 update 方法不使用 @Transactional
- SUPPORTS [如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。]
- server1 update方法使用 @Transactional(propagation = Propagation.REQUIRED),server2 update方法使用@Transactional(propagation = Propagation.SUPPORTS)
- 此时server1和server2的 update方法会在同一个事务中。
- server1 update方法不使用 @Transactional,server2 update方法使用@Transactional(propagation = Propagation.SUPPORTS)
- 此时 server1和server2的update方法是没有事务
- server1 update方法使用 @Transactional(propagation = Propagation.REQUIRED),server2 update方法使用@Transactional(propagation = Propagation.SUPPORTS)
- MANDATORY [如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。(必须运行在事务内)]
- server1 update 方法使用 @Transactional ,server2 update 方法使用@Transactional(propagation = Propagation.MANDATORY)
- 此时如果 server2 中update 方法或 server1 中update方法 出现异常,那么,server1 和 server2 的操作都会回滚
- server1 update 方法使用 @Transactional ,server2 update 方法使用@Transactional(propagation = Propagation.MANDATORY)
- REQUIRES_NEW [总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。]
- server1 update 方法使用 @Transactional ,server2 update 方法使用@Transactional(propagation = Propagation.REQUIRES_NEW)
- 如果server1 update 方法在 server2的操作后 中出现错误,那么 server1的操作会回滚,server2不会回滚
- 此时 是两个事务,server1的update方法开启一个事务,server2的update方法开启一个事务。
- server1 update 方法使用 @Transactional ,server2 update 方法使用@Transactional(propagation = Propagation.REQUIRES_NEW)
- NESTED [如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务, 则按REQUIRED 属性执行]
- server1 Update 方法使用 @Transactional ,server2 update 方法使用@Transactional(propagation = Propagation.NESTED)
- 如果 server 2 update 方法 中出现问题,而server1 的 update方法捕捉到,那么,server2 的操作会被回滚,而server1的操作不会回滚。
- 如果 是server1 的update方法出现错误,那么两个操作都会回滚。
- 主事务和嵌套事务属于同一个事务
- 嵌套事务出错回滚不会影响到主事务
- 主事务回滚会将嵌套事务一起回滚了
- server1 Update 方法使用 @Transactional ,server2 update 方法使用@Transactional(propagation = Propagation.NESTED)
- REQUIRED [如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。(default)]
-
注意
- 传播行为是针对于 两个事务之间的传播
-
-
隔离性
- 简介
属性名称 作用 是否存在脏读 是否存在不可重复读 是否存在幻读 DEFAULT 使用后端数据库默认的隔离级别 READ_UNCOMMITTED 允许读取尚未提交的数据变更(最低的隔离级别) √ √ √ READ_COMMITTED 允许读取并发事务已经提交的数据 √ √ REPEATABLE_READ 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改 √ SERIALIZABLE 最高的隔离级别,完全服从ACID的隔离级别,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的 - 读问题
- 脏读
- 事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据。
- 不可重复读
- 事务 A 多次读取同一数据
- 事务 B 在事务A多次读取的过程中,对数据作了更新并提交
- 导致事务A多次读取同一数据时,结果不一致。
- 幻读
- 系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,
- 但是系统管理员B就在这个时候插入了一条具体分数的记录,
- 当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
- 脏读
- 简介
-
使用注意
@Transactional注解应该只被应用到 public 方法上,这是由Spring AOP的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。
-
使用位置
- 实现类的方法
- 接口类的方法
-
默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为(有例子和结论呦~~⬇⬇⬇⬇)
同一个类调用的测试 @Service public class Server1 { @Resource private DeptMapper deptMapper; @Resource private Server2 server2; public void update () { Dept dept = new Dept(); dept.setDeptno(43); dept.setLoc("中国-1"); dept.setDname("事业部-1"); int i = deptMapper.updateByPrimaryKey(dept); this.update2(); System.out.println(i); } @Transactional public void update2 () { Dept dept = new Dept(); dept.setDeptno(47); dept.setLoc("中国-47"); dept.setDname("事业部-47"); int i = deptMapper.updateByPrimaryKey(dept); int b = 1 / 0; System.out.println(i); } } class TestServer{ /** * 测试 事务的传播行为 和 事务的隔离性 */ @Test public void testNone() { server1.update(); } }
- 例子
- update 方法 上没有@Transactional,而update2 方法上有@Transactional
- 方法 update2 和update方法的修改都不会回滚
- 因为是update2方法和update方法在同一个类中,aop捕获不到,update2方法就不会在事务中进行操作,相当于udpate2方法也没有@Transactional标识。
- update方法上有@Transactional,而update2方法上没有@Transactional
- 此时两个方法修改都会回滚
- 因为调用update方法的是TestServer类中的方法,不在同一个类,可以使用aop代理,此时 update方法就是在事务中进行的。
- update 方法 上没有@Transactional,而update2 方法上有@Transactional
- 例子