Spring事务传播

事务特性

事务有四大特性,分别以下:spring

1. 原子性(Atomicity):事务是数据库逻辑工做单元,事务中包含的操做要么都执行成功,要么都执行失败。数据库

2. 一致性(Consistency):事务执行的结果必须是使数据库数据从一个一致性状态变到另一种一致性状态。当事务执行成功后就说数据库处于一致性状态。若是在执行过程当中发生错误,这些未完成事务对数据库所作的修改有一部分已写入物理数据库,这是数据库就处于不一致状态。并发

3. 隔离性(Isolation):一个事务的执行过程当中不能影响到其余事务的执行,即一个事务内部的操做及使用的数据对其余事务是隔离的,并发执行各个事务之间无不干扰。框架

4. 持续性(Durability):即一个事务执一旦提交,它对数据库数据的改变是永久性的。以后的其它操做不该该对其执行结果有任何影响。函数

事务致使的问题

1. 脏读:指当一个事务正字访问数据,而且对数据进行了修改,而这种数据尚未提交到数据库中,这时,另一个事务也访问这个数据,而后使用了这个数据。由于这个数据尚未提交那么另一个事务读取到的这个数据咱们称之为脏数据测试

2. 不可重复读:指在一个事务内,屡次读同一数据。在这个事务尚未执行结束,另一个事务也访问该同一数据,那么在第一个事务中的两次读取数据之间,因为第二个事务的修改第一个事务两次读到的数据多是不同的,这样就发生了在一个事物内两次连续读到的数据是不同的,这种状况被称为是不可重复读。(针对行内数据字段不一致)this

3. 幻象:一个事务前后读取一个范围的记录,但两次读取的纪录数不一样,咱们称之为幻象读(两次执行同一条 select 语句会出现不一样的结果,第二次读会增长一数据行)(针对查询结果集条数不一致)spa

不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住知足条件的行,解决幻读须要锁表代理

Spring中事务传播属性

Spring中事务很不少功能都是Spring借助底层资源的功能来完成的,可是事务的传播行为是经过Spring框架自身实现的,Spring中定义了7个以PROPAGATION_开头的常量表示它的传播属性,具体以下:code

1. PROPAGATION_REQUIRED:若是存在一个事务,则支持当前事务。若是没有事务则开启

2. PROPAGATION_SUPPORTS:若是存在一个事务,支持当前事务。若是没有事务,则非事务的执行

3. PROPAGATION_MANDATORY:若是已经存在一个事务,支持当前事务。若是没有一个活动的事务,则抛出异常

4. PROPAGATION_REQUIRES_NEW:老是开启一个新的事务。若是一个事务已经存在,则将这个存在的事务挂起

5. PROPAGATION_NOT_SUPPORTED:老是非事务地执行,并挂起任何存在的事务

6. PROPAGATION_NEVER: 老是非事务地执行,若是存在一个活动事务,则抛出异常

7. PROPAGATION_NESTED:若是一个活动的事务存在,则运行在一个嵌套的事务中. 若是没有活动事务, PROPAGATION_REQUIRED 属性执行

Spring中事务的隔离级别

1. ISOLATION_DEFAULT:这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别  

2. ISOLATION_READ_UNCOMMITTED :这是事务最低的隔离级别,它充许别外一个事务能够看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。

3. ISOLATION_READ_COMMITTED :保证一个事务修改的数据提交后才能被另一个事务读取。另一个事务不能读取该事务未提交的数据。这种事务隔离级别能够避免脏读出现,可是可能会出现不可重复读和幻像读。:

4. ISOLATION_REPEATABLE_READ :这种事务隔离级别能够防止脏读,不可重复读。可是可能出现幻像读。

5. ISOLATION_SERIALIZABLE :这是花费最高代价可是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻读。、

事务的隔离级别不一样,有可能产生不一样的错误现象,引用网上经常使用的一张图说明隔离级别与各类问题的关系:

Spring中的事务超时

事务超时指的是一个事务所容许执行的最长时间,超过该时间限制后事务尚未完成,将自动回滚事务。Spring框架中在 TransactionDefinition接口中以 int 的值来表示超时时间,单位是秒。默认设置为底层事务系统的超时值,若是底层数据库事务系统没有设置超时值,那么就是none,没有超时限制

Spring中事务的回滚规则

Spring事务管理器回滚一个事务的时机是在当前事务的上下文中抛出异常时。Spring事务管理器会捕捉到未处理的异常,而后根据规则决定是否须要回滚当前事务。

默认配置下,spring只有在抛出RuntimeException 异常(Errors也会致使事务回滚)时才回滚,而抛出checked异常则不会致使事务回滚。可是咱们也能够明确的配置在抛出哪些异常时回滚事务,包括checked异常(或者配置为不回滚)。

Spring中事务传播行为

下面经过在Springboot中的几个示例,看一下Spring中不一样的事务传播行为。

PROPAGATION_REQUIRED

  1.A方法开启事务,B方法中抛出异常

 1 @Service
 2 public class ProductServiceA {
 3 
 4     @Autowired
 5     private ProductDao productDao;
 6     
 7 @Autowired
 8 private ProductServiceB productServiceB;
 9     
10     @Transactional(propagation = Propagation.REQUIRED)
11     public void A() {
12         productDao.insert();
13         productServiceB.B();
14     }
15 }
16 
17 @Service
18 public class ProductServiceB {
19 
20     @Autowired
21     private ProductDao productDao;
22 
23     @Transactional(propagation = Propagation.REQUIRED)
24     public void B() {
25         productDao.update();
26         throw  new ArithmeticException("测试异常");
27     }
28 }

  测试结果:A和B都没有执行成功

2.A方法开启事务,A方法中抛出异常

 1 @Service
 2 public class ProductServiceA {
 3 
 4     @Autowired
 5     private ProductDao productDao;
 6 
 7 @Autowired
 8 private ProductServiceB productServiceB;
 9 
10     @Transactional(propagation = Propagation.REQUIRED)
11     public void A() {
12         productDao.insert();
13         productServiceB.B();
14 
15         throw  new ArithmeticException("测试异常");
16     }
17 }
18 
19 @Service
20 public class ProductServiceB {
21 
22     @Autowired
23     private ProductDao productDao;
24 
25     @Transactional(propagation = Propagation.REQUIRED)
26     public void B() {
27         productDao.update();
28     }
29 }

测试结果:A和B都没有执行成功

3.A方法没开启事务,A方法中抛出异常

 1 @Service
 2 public class ProductServiceA {
 3 
 4     @Autowired
 5     private ProductDao productDao;
 6 
 7   @Autowired
 8   private ProductServiceB productServiceB;
 9 
10     public void A() {
11         productDao.insert();
12         productServiceB.B();
13         throw  new ArithmeticException("测试异常");
14     }
15 }
16 @Service
17 public class ProductServiceB {
18 
19     @Autowired
20     private ProductDao productDao;
21 
22     @Transactional(propagation = Propagation.NEVER)
23     public void B() {
24         productDao.update();
25     }
26 }

测试结果:A和B都执行成功

4.A方法没开启事务,B方法中抛出异常

 1 @Service
 2 public class ProductServiceA {
 3 
 4     @Autowired
 5     private ProductDao productDao;
 6     @Autowired
 7     private ProductServiceB productServiceB;
 8 
 9     //@Transactional(propagation = Propagation.REQUIRED)
10     public void A() {
11         productDao.insert();
12         productServiceB.B();
13     }
14 }
15 @Service
16 public class ProductServiceB {
17 
18     @Autowired
19     private ProductDao productDao;
20 
21     @Transactional(propagation = Propagation.REQUIRED)
22     public void B() {
23         productDao.update();
24         throw  new ArithmeticException("测试异常");
25     }
26 }

测试结果:A执行成功,B执行失败

综上,PROPAGATION_REQUIRED属性:若是当执行到B方法时,会判断上下文中是否存在事务,若是存在,则加入,此时方法A和方法B用的是同一个事务,方法A和方法B共存亡。若是不存在则方法B会为本身分配一个事务,该事务与方法A无关,因此这种状况可能出现A成功,B执行失败回滚了。PROPAGATION_REQUIRED属性是Spring框架事务传播的默认属性。

RROPAGATION_REQUIRES_NEW

1.A方法没开启事务,B方法中抛出异常

 1 @Service
 2 public class ProductServiceA {
 3 
 4     @Autowired
 5     private ProductDao productDao;
 6     @Autowired
 7     private ProductServiceB productServiceB;
 8 
 9     @Transactional(propagation = Propagation.REQUIRED)
10     public void A() {
11         productDao.insert();
12         productServiceB.B();
13     }
14 }
15 @Service
16 public class ProductServiceB {
17 
18     @Autowired
19     private ProductDao productDao;
20 
21     @Transactional(propagation = Propagation.REQUIRES_NEW)
22     public void B() {
23         productDao.update();
24         throw  new ArithmeticException("测试异常");
25     }
26 }

测试结果:A和B都执行失败

2.A方法没开启事务,A方法自定义四回滚异常,B方法中抛出异常

 1 @Service
 2 public class ProductServiceA {
 3 
 4     @Autowired
 5     private ProductDao productDao;
 6     @Autowired
 7     private ProductServiceB productServiceB;
 8 
 9     //自定义回滚异常
10     @Transactional(propagation = Propagation.REQUIRED,noRollbackFor = ArithmeticException.class)
11     public void A() {
12         productDao.insert();
13         productServiceB.B();
14     }
15 }
16 @Service
17 public class ProductServiceB {
18 
19     @Autowired
20     private ProductDao productDao;
21 
22     @Transactional(propagation = Propagation.REQUIRES_NEW)
23     public void B() {
24         productDao.update();
25         throw  new ArithmeticException("测试异常");
26     }
27 }

测试结果:A方法执行成功,B方法执行失败

3.A方法开启事务,A方法抛异常

 1 @Service
 2 public class ProductServiceA {
 3 
 4     @Autowired
 5     private ProductDao productDao;
 6     @Autowired
 7     private ProductServiceB productServiceB;
 8 
 9     //自定义回滚异常
10     @Transactional(propagation = Propagation.REQUIRED)
11     public void A() {
12         productDao.insert();
13         productServiceB.B();
14         throw  new ArithmeticException("测试异常");
15     }
16 }
17 @Service
18 public class ProductServiceB {
19 
20     @Autowired
21     private ProductDao productDao;
22 
23     @Transactional(propagation = Propagation.REQUIRES_NEW)
24     public void B() {
25         productDao.update();
26 
27     }
28 }

测试结果:A方法执行失败,B方法执行成功

4.A方法开启事务,A方法抛异常

  同PROPAGATION_REQUIRED中示例,B方法单独开启一个事务

5.A方法开启事务,B方法抛异常

  同PROPAGATION_REQUIRED中示例,B方法单独开启一个事务

综上,RROPAGATION_REQUIRES_NEW属性老是开启一个新的事务,若是已经存在一个事务,则将这个事务挂起。PROPAGATION_REQUIRED 和RROPAGATION_REQUIRES_NEW的事务传递的区别在于后者老是新起一个事务,因此可能存在两个独立的事务。

PROPAGATION_SUPPORTS

当方法B加上@Transactional(propagation = Propagation.SUPPORTS)注解,执行到方法B时,会检查上下文中是否是已经存在事务,存在则加入,此时至关于PROPAGATION_REQUIRED,不存在则以非事务方法运行,此时恰好和RROPAGATION_REQUIRES_NEW相反。这种方式总结起来就是有事务就加入,没有就算。

PROPAGATION_NOT_SUPPORTED

当方法B加上@Transactional(propagation = Propagation.NOT_SUPPORTED)注解,执行方法B时,检查上下文中有没有事务,若是没有则正常以非事务方式执行,若是有事务,则挂起当前事务,继续以非事务方式执行完,而后在继续执行当前事务。这种方式总结起来就是有事务也不支持。方法B必定以非事务方式运行,不存在回滚方法B的可能。

PROPAGATION_NESTED

1.A方法抛出异常

 1 @Service
 2 public class ProductServiceA {
 3 
 4     @Autowired
 5     private ProductDao productDao;
 6     @Autowired
 7     private ProductServiceB productServiceB;
 8 
 9     //自定义回滚异常
10     @Transactional(propagation = Propagation.REQUIRED)
11     public void A() {
12         productDao.insert();
13         productServiceB.B();
14         throw  new ArithmeticException("测试异常");
15     }
16 }
17 @Service
18 public class ProductServiceB {
19 
20     @Autowired
21     private ProductDao productDao;
22 
23     @Transactional(propagation = Propagation.NESTED)
24     public void B() {
25         productDao.update();
26 
27     }
28 }

测试结果:A和B都执行失败。

2.B方法抛出异常,主事务可选择性处理该异常

 1 @Service
 2 public class ProductServiceA {
 3 
 4     @Autowired
 5     private ProductDao productDao;
 6     @Autowired
 7     private ProductServiceB productServiceB;
 8 
 9     //自定义回滚异常
10     @Transactional(propagation = Propagation.REQUIRED,noRollbackFor = ArithmeticException.class)
11     //@Transactional(propagation = Propagation.REQUIRED)
12     public void A() {
13         productDao.insert();
14         productServiceB.B();
15 
16     }
17 }
18 @Service
19 public class ProductServiceB {
20 
21     @Autowired
22     private ProductDao productDao;
23 
24     @Transactional(propagation = Propagation.NESTED)
25     public void B() {
26         productDao.update();
27         throw  new ArithmeticException("测试异常");
28     }
29 }

当方法B加上@Transactional(propagation = Propagation.NESTED)注解,执行方法B时会先判断上下文中是否是存在事务,若是不存在则,至关于RROPAGATION_REQUIRES_NEW属性,新起一个事务运行,若是存在,则在当前事务中嵌套一个事务,此时也出现两个事务,是嵌套关系。若是主事务提交或者回滚,则嵌套事务也将提交或者回滚。嵌套事务异常,主事务可选择回滚仍是继续执行,主要是看如何处理这个异常。

PROPAGATION_NESTED与RROPAGATION_REQUIRES_NEW的区别是PROPAGATION_NESTED建立的是外部事务的子事务, 若是外部事务commit/roolback, 嵌套事务也会被commit/rollback,而RROPAGATION_REQUIRES_NEW建立的是一个新的事务,与已存在的事务独立,即方法A和方法B是分别独立的两个事务,方法B若是已经执行,此时方法A抛异常将不影响方法B。

PROPAGATION_NEVER

这种方式代表必须以非事务的方式运行,若是存在事务,则抛异常。

PROPAGATION_MANDATORY

这个和PROPAGATION_NEVER相反,必须以事务的方式运行,不存在则抛异常。

事务的传播行为失效问题

若是在程序中出如今同一个类的内部出现调用另外一个@Transactional注解函数,或者在private或者protected方法上使用@Transactional注解,则将可能出现传播行为不起做用。

缘由:因为Spring的事务是经过AOP来实现的,Spring中经过动态代理实现Aop功能,不论是jdk动态代理仍是cglib动态代理,都不会处理目标类的private和protected方法。而若是在类的内部调用另一个添加了@Transactional注解的函数,至关于经过this调用函数,而不是代理对象调用该函数,因此Spring也不会处理这个传播行为。

相关文章
相关标签/搜索