# spring-transaction **Repository Path**: dtzds/spring-transaction ## Basic Information - **Project Name**: spring-transaction - **Description**: 有关于spring事务使用demo - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-02-26 - **Last Updated**: 2021-04-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ![输入图片说明](https://images.gitee.com/uploads/images/2021/0423/114155_37885f9f_5223391.png "1.png") ## 隔离级别 >[info] spring的默认隔离级别为ISOLATION_DEFAULT,即数据库的隔离级别 | **隔离级别** | **描述** | | --------------- | ------------------------------------------------------------ | | DEFAULT | 使用数据库本身使用的隔离级别 ORACLE(读已提交) MySQL(可重复读) | | READ_UNCOMITTED | 读未提交(脏读)最低的隔离级别,一切皆有可能。 | | READ_COMMITED | 读已提交,ORACLE默认隔离级别,有幻读以及不可重复读风险。 | | REPEATABLE_READ | 可重复读,解决不可重复读的隔离级别,但还是有幻读风险。 | | SERLALIZABLE | 串行化,最高的事务隔离级别,不管多少事务,挨个运行完一个事务的所有子事务之后才可以执行另外一个事务里面的所有子事务,这样就解决了脏读、不可重复读和幻读的问题了 | ## 传播行为 ### spring事务7大传播行为 >[info] 事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。 | **事务传播行为类型** |**说明** | | ------------------------- | ------------------------------------------------------------ | | PROPAGATION_REQUIRED | 表示当前方法必须运行在事务中。如果当前存在事务,则沿用该事务运行,否则开启一个新的事务 | | PROPAGATION_SUPPORTS | 表示当前方法支持在事务中运行,如果当前不存在事务,则以非事务的形式运行 | | PROPAGATION_MANDATORY | 表示当前方法必须运行在事务中,如果当前不存在事务,则抛出一个异常 | | PROPAGATION_REQUIRES_NEW | 表示当前方法必须运行在一个单独的事务中。如果当前存在一个事务,则该事务将会被挂起。两个事务相互独立运行。| | PROPAGATION_NOT_SUPPORTED | 表示当前方法不支持在事务中运行,如果当前存在一个事务,则将该事务挂起 | | PROPAGATION_NEVER | 表示当前方法不支持在事务中运行,如果当前存在一个事务,则抛出一个异常 | | PROPAGATION_NESTED | 如果当前存在一个事务,那么该方法将会在嵌套事务中运行;如果当前不存在事务,则与PROPAGATION_REQUIRED行为一致 | #### 1、PROPAGATION_REQUIRED > 如果当前存在事务,则沿用当前事务;否则,开启一个新的事务。 ![输入图片说明](https://images.gitee.com/uploads/images/2021/0423/114330_4ffcfef0_5223391.png "2.png") 如图所示,方法A调用方法B,方法B的传播行为为PROPAGATION_REQUIRED,即如果方法A存在一个事务,则方法B会沿用方法A的事务;如果方法A不存在事务,则方法B会开启一个新的事务执行方法B。 故存在如下几种情况: 1)方法A不存在事务,方法B抛出异常,则方法B回滚,方法A不会回滚; 2)方法A存在事务,方法A/B抛出异常,方法A、B都会回滚;若方法A将方法B抛出的异常进行捕获,则会抛出异常:org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only #### 2、PROPAGATION_SUPPORTS >[info] 如果当前存在事务,则沿用该事务运行;否则,以非事务的形式运行 ![输入图片说明](https://images.gitee.com/uploads/images/2021/0423/114342_dbfdcc5f_5223391.png "3.png") 方法A调用方法B,方法B的传播行为为PROPAGATION_SUPPORTS,即如果方法A存在一个事务,方法B会沿用方法A的事务;如果方法A不存在事务,则方法B以非事务的方式运行。 故存在如下几种情况: 1)方法A不存在事务时,两个方法都不会以事务的形式运行,故都不会回滚 2)方法A存在事务时,方法A/B抛出异常,方法A、B都会回滚;若方法A将方法B抛出的异常进行捕获,则会抛出异常:org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only #### 3、PROPAGATION_MANDATORY >[info] 如果当前存在事务,则沿用该事务运行;否则抛出一个异常 ![输入图片说明](https://images.gitee.com/uploads/images/2021/0423/114356_7938456e_5223391.png "4.png") 方法A调用方法B,方法B的传播行为为PROPAGATION_MANDATORY,即如果方法A存在一个事务,方法B会沿用方法A的事务;如果方法A不存在事务,则方法B会抛出一个异常; 故存在如下几种情况: 1)方法A不存在事务,则抛出异常IllegalTransactionStateException:No existing transaction found for transaction marked with propagation 'mandatory' 2)方法A存在事务,A/B抛出异常,A、B都回滚;如果B抛出异常被A捕获,则抛出UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only #### 4、PROPAGATION_REQUIRES_NEW >[info] 不管当前是否存在事务,都会创建一个新的 **独立** 的事务,如果当前存在事务,则该事务会被挂起 ![输入图片说明](https://images.gitee.com/uploads/images/2021/0423/114408_576c5e0a_5223391.png "5.png") 方法A调用方法B,方法B的传播行为为PROPAGATION_REQUIRES_NEW;由于PROPAGATION_REQUIRES_NEW的传播行为行为会创建一个独立的事务;故可能存在如下几种情况: 1) 方法A不存在事务,若B抛出异常,则方法B回滚,方法A不会回滚 2)如果方法A存在事务,若方法A抛出异常,则方法A回滚,方法B正常执行;若方法B抛出异常,且未被方法A捕获,则方法A、B都回滚;若方法B抛出的异常被方法A捕获,则方法B回滚,方法A正常执行 #### 5、PROPAGATION_NOT_SUPPORTED >[info] 当前方法必须在非事务的环境中运行,如果存在事务,则将该事务挂起 ```java @Transactional public void methodA() { b.methodB(); } @Transactional(propagation = Propagation.NOT_SUPPORTED) public void methodB() { // .... } ``` 方法A调用方法B,方法B的传播行为为PROPAGATION_NEVER,由于PROPAGATION_NEVER传播行为必须运行在非事务环境中,故存在以下几种情况: 1)方法A不存在事务,则两个方法都不涉及事务,则方法A、B正常执行 2)方法A存在事务,由于方法B是运行在非事务的环境下,若方法B抛出异常,方法A不捕获的话,则方法A回滚,方法B抛出异常前正常执行;若方法A捕获方法B抛出的异常,则方法A不受影响 #### 6、PROPAGATION_NEVER >[info] 当前方法必须运行在非事务的环境中,如果存在事务,则抛出异常 ```java @Transactional public void methodA() { b.methodB(); } @Transactional(propagation = Propagation.NEVER) public void methodB() { // .... } ``` 即如果方法A存在一个事务,则将会抛出异常:org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never' #### 7、PROPAGATION_NESTED >[info] 如果当前存在一个事务,那么该方法将会在嵌套事务中运行;如果当前不存在事务,则与PROPAGATION_REQUIRED行为一致 ```java @Transactional public void methodA() { b.methodB(); } @Transactional(propagation = Propagation.NESTED) public void methodB() { // .... } ``` 使用PROPAGATION_NESTED需要明确一个嵌套事务的概念,即内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。 1)如果当前不存在事务,若B抛出异常,则B回滚;若A抛出异常,则A、B都不回滚 2)如果当前存在事务,若A抛出异常,则A、B都回滚;若B抛出异常,且未被A捕获,则A、B都回滚;若被A捕获,则B回滚,A不会回滚 ## rollbackFor 默认抛出RuntimeException和Error异常会进行回滚,其它异常则不会 ## readOnly 开启readOnly=true,则表明该事务为只读事务,部分数据库如:oracle对于只读事务将不会进行回滚操作,并且不会记录日志 ## 常见事务问题 ### 1、方法不为public ```java public void methodA() { methodB(); } // 需要将此处的private修改为public才会生效 @Transactional private void methodB() { // TODO } ``` >[danger] 注意:`@Transactional` 注解必须添加在`public`修饰的方法上,否则事务不会生效 ### 2、事务自调用问题 >[info] 事务自调用问题,即同一个类中,一个方法调用另一个带有事务的方法 ```java public class TransactionTest { public void methodA() { this.methodB(); } @transactional public void methodB() { // TODO } } ``` > 由于spring 事务是使用Aop实现的,而AOP是基于动态代理实现的;在自调用的过程中,调用的是类自身的方法,而不是代理类的方法;即methodA方法调用methodB方法实际上是对于类TransactionTest类中methodB方法调用,而不是调用TransactionTest代理类中的methodB方法 #### 解决方式一 > 引入自身bean ```java public class TransactionTest { @Autowired @Lazy private TransactionTest transactionTest; public void methodA() { transactionTest.methodB(); } @transactional public void methodB() { // TODO } } ``` #### 解决方式二 > 通过ApplicationContext引入bean ```java public class TransactionTest { @Autowired private ApplicationContext applicationContext; public void methodA() { applicationContext.getBean(TransactionTest.class).methodB(); } @transactional public void methodB() { // TODO } } ``` #### 解决方式三 > 通过aopContext获取当前代理对象 ```java public class TransactionTest { @Autowired private ApplicationContext applicationContext; public void methodA() { TransactionTest context = (TransactionTest) AopContext.currentProxy(); context.methodB(); } @transactional public void methodB() { // TODO } } ```