1. 如何使用Spring管理事务

Spring框架在启动的时候会创建相关的bean实例对象,并且会扫描标注有相关注解的类和方法,为这些方法生成代理对象。如果扫描到标注了@Transactional 注解的类或者方法时,会为标注了@Transactional注解的类或者方法生成代理对象,在操作数据库时会通过代理对象来开启事务、提交事务或者回滚事务,无需开发人员关心开启事务、提交、回滚事务的细节

  1. @Transactional(rollbackFor=Exception.class)
  2. public void saveUser(User user){
  3. }

2. 事务分类

Spring 管理的事务可以分为逻辑事务和物理事务两大类

  1. 逻辑事务:通常指通过Spring等框架管理的事务,这种事务时建立在物理事务之上的,比物理事务更加抽象
  2. 物理事务:通常指的是针对特定数据库的事务

Spring 支持两种事务声明方式,分别是编程式事务和声明式事务

  1. 编程式事务:如果系统需要明确的事务,并且需要细粒度的控制各个事务的边界,此时建议使用编程式事务
  2. 声明式事务:如果系统对于事务控制力度较为粗糙,则建议使用声明式事务,一般通过声明一个事务来控制所有跟声明相关的事务

3. Spring 事务三大接口

Spring 支持事务的管理功能,最核心的就是Spring 事务的三大接口,分别是:PlatformTransactionManager、TransactionDefinition和TransactionStatus。

3.1. PlatformTransactionManager 接口

Spring 并不直接管理事务,而是提供多种事务管理器。Spring将这些事务管理器委托给Hibernate、MyBatis等持久化框架来实现事务管理
PlatformTransactionManager 接口是Spring提供的一个事务管理器接口

  1. package org.springframework.transaction;
  2. import org.springframework.lang.Nullable;
  3. public interface PlatformTransactionManager extends TransactionManager {
  4. /**
  5. * 获取事务状态
  6. */
  7. TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
  8. throws TransactionException;
  9. /**
  10. * 提交事务
  11. */
  12. void commit(TransactionStatus status) throws TransactionException;
  13. /**
  14. * 回滚事务
  15. */
  16. void rollback(TransactionStatus status) throws TransactionException;
  17. }

3.2. TransactionDefinition 接口

TransactionDefinition接口定义了与事务相关的方法,表示事务属性的常量等信息。部分事务属性的常量与Propagation枚举类中的事务传播类型相对应

  1. package org.springframework.transaction;
  2. public interface TransactionDefinition {
  3. /**
  4. * 支持当前事务,若当前没有事务就创建一个新的事务
  5. */
  6. int PROPAGATION_REQUIRED = 0;
  7. /**
  8. * 如果当前存在事务,则加入该事务,如果当前没有事务,则以非事务的方式继续运行
  9. */
  10. int PROPAGATION_SUPPORTS = 1;
  11. /**
  12. * 如果当前存在事务,则加入该事务,如果当前没有事务则抛出异常
  13. */
  14. int PROPAGATION_MANDATORY = 2;
  15. /**
  16. * 创建一个新事务,如果当前存在事务,则把当前事务挂起
  17. */
  18. int PROPAGATION_REQUIRES_NEW = 3;
  19. /**
  20. * 以非事务的方式运行,如果当前存在事务,则把当前事务挂起
  21. */
  22. int PROPAGATION_NOT_SUPPORTED = 4;
  23. /**
  24. * 以非事务的方式运行,如果当前事务存在,则抛出异常
  25. */
  26. int PROPAGATION_NEVER = 5;
  27. /**
  28. * 如果当前正有一个事务在运行中,则该方法运行在一个嵌套的事务中
  29. * 被嵌套的事务可以独立于封装的事务进行提交或者回滚(这里需要事务的保存点)
  30. * 如果封装的事务不存在,后续事务行为同PROPAGATION_REQUIRES_NEW
  31. */
  32. int PROPAGATION_NESTED = 6;
  33. /**
  34. * 使用后端数据库默认的隔离级别
  35. */
  36. int ISOLATION_DEFAULT = -1;
  37. /**
  38. * 最低的隔离级别
  39. */
  40. int ISOLATION_READ_UNCOMMITTED = 1; // same as java.sql.Connection.TRANSACTION_READ_UNCOMMITTED;
  41. /**
  42. * 防止脏读,但可能残生幻读或不可重复读的问题
  43. */
  44. int ISOLATION_READ_COMMITTED = 2; // same as java.sql.Connection.TRANSACTION_READ_COMMITTED;
  45. /**
  46. * 可以阻止脏读和不可重复读,但是可能产生幻读
  47. */
  48. int ISOLATION_REPEATABLE_READ = 4; // same as java.sql.Connection.TRANSACTION_REPEATABLE_READ;
  49. /**
  50. * 可以防止脏读、不可重复读和幻读
  51. */
  52. int ISOLATION_SERIALIZABLE = 8; // same as java.sql.Connection.TRANSACTION_SERIALIZABLE;
  53. /**
  54. * 使用默认超时时间
  55. */
  56. int TIMEOUT_DEFAULT = -1;
  57. /**
  58. * 获取事务的传播行为
  59. */
  60. default int getPropagationBehavior() {
  61. return PROPAGATION_REQUIRED;
  62. }
  63. /**
  64. * 获取事务的隔离级别
  65. */
  66. default int getIsolationLevel() {
  67. return ISOLATION_DEFAULT;
  68. }
  69. /**
  70. * 获取事务的超时时间
  71. */
  72. default int getTimeout() {
  73. return TIMEOUT_DEFAULT;
  74. }
  75. /**
  76. * 返回当前是否为只读事务
  77. */
  78. default boolean isReadOnly() {
  79. return false;
  80. }
  81. /**
  82. * 获取事务名称
  83. */
  84. @Nullable
  85. default String getName() {
  86. return null;
  87. }
  88. static TransactionDefinition withDefaults() {
  89. return StaticTransactionDefinition.INSTANCE;
  90. }
  91. }

3.3. TransactionStatus 接口

TransactionStatus 接口主要用来存储事务执行失败的状态,并且定义了一组方法,用来判断或者读取事务的状态信息

  1. public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
  2. /**
  3. * 是否有保存点
  4. */
  5. boolean hasSavepoint();
  6. /**
  7. * 将事务设计的数据刷新到磁盘
  8. */
  9. @Override
  10. void flush();
  11. }
  1. public interface TransactionExecution {
  2. /**
  3. * 判断是否是新事务
  4. */
  5. boolean isNewTransaction();
  6. /**
  7. * 设置为只读回滚
  8. */
  9. void setRollbackOnly();
  10. /**
  11. * 是否为只读回滚
  12. */
  13. boolean isRollbackOnly();
  14. /**
  15. * 判断当前事务是否已经完成
  16. */
  17. boolean isCompleted();
  18. }

4. Spring事务隔离级别

4.1. ISOLATION_DEFAULT 隔离级别

ISOLATION_DEFAULT 隔离级别是Spring中PlatformTransactionManager默认的事务隔离级别。当Spring 的事务隔离级别设置为ISOLATION_DEF,AULT时,Spring不做事务隔离级别处理,直接会使用数据库默认的事务隔离级别

4.2. ISOLATION_READ_UNCOMMITTED 隔离级别

ISOLATION_READ_COMMITTED 隔离级别是Spring中最低的隔离级别,当Spring 中设置为该隔离级别时,会出现事务A可以读取到事务B未提交的数据。这种隔离级别会产生脏读、不可重复读和幻读的问题。相当于MySQL中的未提交读隔离级别

4.3. ISOLATION_REPEATABLE_READ隔离级别

ISOLATION_REPEATABLE_READ隔离级别可以解决幻读、不可重复读的问题,但是可能会产生幻读的问题。事务A第一次按照条件从数据库中查询出数据后,此时事务B向数据库中插入了符合事务A查询条件的数据,事务A第二次再向数据库查询时,会将事务B插入的数据查询出来,这就是幻读现象。相当于MySQL中的可重复度隔离级别

4.4. ISOLATION_SERIALIZABLE 隔离级别

再ISOLATION_SERIALIZABLE 隔离级别下,事务只能够按照特定的顺序执行,也就是多个事务之间只能够按照串行顺序执行。这是最可靠的隔离级别,然而这种可靠性付出了并发性的代价。相当于MySQL中的串行化隔离级别

5. Spring的7种事务传播类型

5.1. REQUIRED 事务传播类型

REQUIRED 事务传播类型表示如果当前没有事务,就创建一个事务,如果已经存在一个事务,就加入这个事务。这是最常见的事务传播类型,也是Spring当中默认的事务传播类型。

  1. @Transactional(propagation = Propagation.REQUIRED)

5.2. REQUIRED_NEW事务传播类型

REQUIRED_NEW 事务传播类型表示当前存在事务,则把当前事务挂起,并重新创建新的事务执行,直到新的事务提交或者回滚,才会恢复执行原来的事务。这种事务传播类型具备隔离性,将原有事务和新创建的事务隔离,原有事务和新创建的事务的提交和回滚互不影响。新创建的事务和被挂起的事务没有任何关系,他们是两个不相干的独立事务。

  1. @Transactional(propagation = Propagation.REQUIRES_NEW)

5.3. SUPPORTS事务传播类型

SUPPORTS 事务传播类型标识支持当前事务,如果当前没有事务,就以非事务的方式执行.外部不存在事务时,不会开启新的事务,外部存在事务时,将其加入外部事务

  1. @Transactional(propagation = Propagation.SUPPORTS)

5.4. MANDATORY 事务传播类型

MANDATORY 事务传播类型表示支持当前事务,这种事务传播类型具备强制性,当前操作必须存在事务,如果不存在就抛出异常

  1. @Transactional(propagation = Propagation.MANDATORY)

5.5. NOT_SUPPORTED事务传播类型

NOT_SUPPORTED 事务传播类型表示以非事务方式执行,如果当前操作在一个事务中,则把当前事务挂起,直到当前操作完成再恢复事务执行.如果当前操作存在事务,则把事务挂起,以非事务的方式运行

  1. @Transactional(propagation = Propagation.NOT_SUPPORTED)

5.6. NEVER 事务传播类型

NEVER事务传播类型表示以非事务的方式执行,如果当前操作存在事务,则抛出异常.
NEVER事务传播类型和NOT_SUPPORTED事务传播类型的区别在于当前存在事务则NEVERY事务传播类型会抛出异常,而NOT_SUPPORTED事务传播类型会把当前事务挂起,以非事务的方式执行
NEVERY事务传播类型和MANDATORY事务传播类型的区别是NEVERY事务传播类型表示如果当前操作存在事务则抛出异常,而MANDATORY事务传播类型表示如果当前操作不存再事务则抛出异常

  1. @Transactional(propagation = Propagation.NEVER)

5.7. NESTED事务传播类型

NESTED 事务传播类型表示如果当前方法有一个事务正在运行,则这个方法应该运行再一个嵌套事务中,被嵌套的事务可以独立于被封装的事务进行提交或者回滚.如果没有活动事务,则按照REQUIRED 事务传播类型执行
如果封装事务存在,并且外层事务抛出异常回滚,那么内层事务必须回滚.如果内层事务回滚,则并不影响外层事务的提交和回滚.如果封装事务不存在,则按照REQUIRED事务传播类型执行

  1. @Transactional(propagation = Propagation.NESTED)