Spring 事务

1. 使用事务

Spring 支持两种类型的事务,声明式事务和编程式事务。

  • 编程式事务:类似于 Jdbc 事务的写法,需要将事务的代码嵌入到业务逻辑中,这样代码的耦合度较高。
  • 声明式事务:通过AOP的思想能够有效的将事务和业务逻辑代码解耦,因此在实际开发中,声明式事务得到了广泛的应用,而编程式事务则较少使用
    @Service
    public class DemoService {
      @Autowired
      DataSourceTransactionManager txManager;
      // 1. 编程式事务
      public void saveDb() {
          // 设置事务属性
          DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
          // 开启一个事务
          TransactionStatus status = txManager.getTransaction(definition);
          try {
              // 操作数据库
              // 提交事务
              txManager.commit(status);
          } catch (DataAccessException e) {
              e.printStackTrace();
              // 回滚事务
              txManager.rollback(status);
          }
      }
      // 2.声明式事务
      @Transactional
      public void saveDb2() {
        // 操作数据库
      }
    }
    

2. Spring 事务核心代码

Spring对事务的支持提供了3个核心基础类,分别如下:

  1. PlatformTransactionManager
  2. TransactionDefinition
  3. TransactionStatus

2.1 PlatformTransactionManager

核心逻辑处理。根据ORM框架的不同,事务管理器的实现类也不同

  1. JDBC 和 mybatis的是 DataSourceTransactionManager
  2. Hibernate HibernateTransactionManager
  3. JPA JpaTransactionManager
// 定义事务管理器
@Bean(name = "dataSourceTx")
public DataSourceTransactionManager dataSourceTx(@Qualifier("dataSource") DataSource dataSource) {
   DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
   dataSourceTransactionManager.setDataSource(dataSource);
   return dataSourceTransactionManager;
}

提供的能力如下:

public interface PlatformTransactionManager extends TransactionManager {
    // 1.开启事务
	TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
	// 2.提交事务
	void commit(TransactionStatus status) throws TransactionException;
    // 3.回滚事务
	void rollback(TransactionStatus status) throws TransactionException;
}

2.2 TransactionDefinition

定义当前事务的属性。比如,隔离级别、传播规则、回滚策略、超时时间、是否只读

public interface TransactionDefinition {
    // 传播规则
	int PROPAGATION_REQUIRED = 0;
	int PROPAGATION_SUPPORTS = 1;
	int PROPAGATION_MANDATORY = 2;
	int PROPAGATION_REQUIRES_NEW = 3;
	int PROPAGATION_NOT_SUPPORTED = 4;
	int PROPAGATION_NEVER = 5;
	int PROPAGATION_NESTED = 6;
    // 隔离级别
	int ISOLATION_DEFAULT = -1;
	int ISOLATION_READ_UNCOMMITTED = 1;  	
	int ISOLATION_READ_COMMITTED = 2;  		
	int ISOLATION_REPEATABLE_READ = 4;  	
	int ISOLATION_SERIALIZABLE = 8;
	// 超时时间,默认-1,不超时
	int TIMEOUT_DEFAULT = -1;
    // 获取事务的传播性
	default int getPropagationBehavior() {
		return PROPAGATION_REQUIRED;
	}
    // 获取事务的隔离级别
	default int getIsolationLevel() {
		return ISOLATION_DEFAULT;
	}
    // 获取事务的超时时间
	default int getTimeout() {
		return TIMEOUT_DEFAULT;
	}
    // 获取事务是否是只读事务
	default boolean isReadOnly() {
		return false;
	}
    // 获取事务的名称
	default String getName() {
		return null;
	}
	static TransactionDefinition withDefaults() {
		return StaticTransactionDefinition.INSTANCE;
	}
}

2.3 TransactionStatus

事务的状态,可以理解为事务本身的属性

public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
   /**
    * 是否有Savepoint Savepoint是当事务回滚时需要恢复的状态
    */
   boolean hasSavepoint();
   /**
    * flush()操作和底层数据源有关,并非强制所有数据源都要支持
    */
   void flush();
   /**
    * 是否是新事务(或是其他事务的一部分)
    */
   boolean isNewTransaction();
   /**
    * 设置rollback-only 表示之后需要回滚
    */
   void setRollbackOnly();
   /**
    * 是否rollback-only
    */
   boolean isRollbackOnly();
   /**
    * 判断该事务已经完成
    */
   boolean isCompleted();
   /**
    * 创建一个Savepoint
    */
   Object createSavepoint() throws TransactionException;
   /**
    * 回滚到指定Savepoint
    */
   void rollbackToSavepoint(Object savepoint) throws TransactionException;
   /**
    * 释放Savepoint 当事务完成后,事务管理器基本上自动释放该事务所有的savepoint
    */
   void releaseSavepoint(Object savepoint) throws TransactionException;
}

3.事务实现原理

3.1 开启事务

有两种方式

  1. 使用注解 @EnableTransactionManagement 声明开启事务。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
    // 用来表示默认使用JDK Dynamic Proxy还是CGLIB Proxy
	boolean proxyTargetClass() default false;
	
	// 表示以Proxy-based方式实现AOP还是以Weaving-based方式实现AOP
	AdviceMode mode() default AdviceMode.PROXY;
	
	// 顺序
	int order() default Ordered.LOWEST_PRECEDENCE;
}
  1. 通过在 DataSource 配置的时候,通过@Bean 配置 DataSourceTransactionManager。
    在spring boot 中,我们添加了数据库配置、创建了 DataSourceTransactionManager 后,会触发spring.factories 配置类的加载条件。
// 配置1。自动配置事务管理器
// 触发条件:使用了jdbcTemplate或者配置了数据库事务。
// 触发事件:自动将环境中的配置加载到 DataSourceProperties 中
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class })
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceTransactionManagerAutoConfiguration {
}

// 配置2:事务自动配置
// 触发条件:1. 配置了数据库事务。 2.在等待 AutoConfigureAfter 中指定的配置加载完成后才开始加载
// 触发事件:自动加载事务的配置
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(PlatformTransactionManager.class)
@AutoConfigureAfter({ JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
        DataSourceTransactionManagerAutoConfiguration.class, Neo4jDataAutoConfiguration.class })
@EnableConfigurationProperties(TransactionProperties.class)
public class TransactionAutoConfiguration {

   // 内部类,配置3:配置事务的代理方式以及注册事务的注解 EnableTransactionManagement
   @Configuration(proxyBeanMethods = false)
   @ConditionalOnBean(TransactionManager.class)
   @ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
   public static class EnableTransactionManagementConfiguration {
      
      @Configuration(proxyBeanMethods = false)
      @EnableTransactionManagement(proxyTargetClass = false)
      @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
              matchIfMissing = false)
      public static class JdkDynamicAutoProxyConfiguration {

      }

      @Configuration(proxyBeanMethods = false)
      @EnableTransactionManagement(proxyTargetClass = true)
      @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
              matchIfMissing = true)
      public static class CglibAutoProxyConfiguration {

      }
   }
}

3.2 注册事务配置器

通过@Import引入了另一个配置TransactionManagentConfigurationSelector。
在Spring启动的时候,向容器注册BeanDefinition的。注册的触发代码是 ConfigurationClasspathPostProcessor 调用 ConfigurationClassParser 触发。

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
	@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] {AutoProxyRegistrar.class.getName(),
						ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {determineTransactionAspectClass()};
			default:
				return null;
		}
	}

	private String determineTransactionAspectClass() {
		return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
				TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
				TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
	}
}

我们看到,事务的代理模式有2种

  1. PROXY。 JDK proxy-based advice。注册了2个配置类。
    1. AutoProxyRegistrar。向容器注册了 InfrastructureAdvisorAutoProxyCreator。这是 AbstractAdvisorAutoProxyCreator 的子类,BeanPostProcessor 实现类。用于创建代理对象。
    2. ProxyTransactionManagementConfiguration,是个配置类
      1. TransactionalEventListenerFactory
      2. BeanFactoryTransactionAttributeSourceAdvisor。包括 TransactionAttributeSourcePointcut(filter和matches之类的方法,用来匹配被代理类) 和 TransactionInterceptor(事务逻辑核心,aop的拦截器) 两个子类。
      3. TransactionAttributeSource。TransactionDefinition 的扩展,增加了isCandidateClass()的方法(可以用来帮助Pointcut匹配)。实现类时 AnnotationTransactionAttributeSource
      4. TransactionInterceptor。我们事务实现逻辑的切面
  2. ASPECTJ。 AspectJ weaving-based advice。是一种 编译期织入 实现的方式。比较少用

3.3 启动流程

与aop的启动流程一致。 InfrastructureAdvisorAutoProxyCreator 的作用和 AnnotationAwareAspectJAutoProxyCreator 是一样的。

public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {
    @Nullable
    private ConfigurableListableBeanFactory beanFactory;
    @Override
    protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        super.initBeanFactory(beanFactory);
        this.beanFactory = beanFactory;
    }
    //在Spring容器中获取Advisor的Bean,然后判断是否是内部的bean对象。
    @Override
    protected boolean isEligibleAdvisorBean(String beanName) {
        return (this.beanFactory != null && this.beanFactory.containsBeanDefinition(beanName) &&
                this.beanFactory.getBeanDefinition(beanName).getRole() == BeanDefinition.ROLE_INFRASTRUCTURE);
    }
}

3.4 使用事务

当我们调用开始事务的方法时,本质是调用代理对象。代理对象 调用 aop拦截器的 invoke方法TransactionInterceptor的核心代码如下

@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation) throws Throwable {
   TransactionAttributeSource tas = getTransactionAttributeSource();
   final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
   final TransactionManager tm = determineTransactionManager(txAttr);
   //省略部分代码
   
   //获取事物管理器
   PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
   final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
   
   if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
      // 打开事务(内部就是getTransactionStatus的过程)
      TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
   
      Object retVal;
      try {
          // 执行业务逻辑 invocation.proceedWithInvocation();
      } catch (Throwable ex) {
          // 异常回滚
          completeTransactionAfterThrowing(txInfo, ex);
          throw ex;
      } finally {
          cleanupTransactionInfo(txInfo);
      }
   
      //省略部分代码
   
      //提交事物
      commitTransactionAfterReturning(txInfo);
      return retVal;
   }
}

事务实现原理: https://www.cnblogs.com/insaneXs/p/13638034.html

4.事务使用规范

【没有事务】

  1. 必须使用Spring管理的bean,且使用@Transactional注解声明
  2. DataSource必须开启事务管理器,否则事务都是自动提交不开启事务。
  3. 数据库必须支持事务。

【事务失效】

  1. 不要在接口上声明@Transactional ,而要在具体类的方法上使用 @Transactional 注解,否则注解可能无效
  2. 调用开启事务的对象的时候,必须调用代理对象。所有对象内部方法间的调用不走代理,事务就不生效。
  3. 方法中开启新的线程,因为事务是基于ThreadLocal实现的,所以这个线程无法继承父线程的事务。
  4. 基于接口动态代理的 AOP 事务(JdkProxy),方法必须是public,不能是static,
  5. 基于CGLib 字节码动态代理的方案是通过扩展被增强类,动态创建其子类的方式进行 AOP 增强植入的。 由于使用 final、static、private 修饰符的方法都不能被子类覆盖,这些方法将无法实施 AOP 增强。 所以方法不能是private,不能是final和static。

【事务存在,但回滚失败】

  1. 异常没有往外抛出,自己捕获异常
  2. 抛出受检异常。正常只有抛出运行时异常才会回滚,比如runtimeException和NullPointerException。 所以我们可以自己设置允许回滚的类:@Transactional(rollbackFor = FileNotFoundException.class)

5.声明式事务

5.1.隔离级别

org.springframework.transaction.annotation.Isolation 枚举类中定义了五个表示隔离级别的值:

使用方式 : @Transactional(isolation = Isolation.DEFAULT)

public enum Isolation {
   DEFAULT(-1),
   READ_UNCOMMITTED(1),
   READ_COMMITTED(2),
   REPEATABLE_READ(4),
   SERIALIZABLE(8);
}

5.2.传播级别

所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。

org.springframework.transaction.annotation.Propagation枚举类中定义了7个表示传播行为的枚举值:

public enum Propagation {
   REQUIRED(0),
   SUPPORTS(1),
   MANDATORY(2),
   REQUIRES_NEW(3),
   NOT_SUPPORTED(4),
   NEVER(5),
   NESTED(6);
}
  • REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  • SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  • REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  • NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
  • NESTED:如果当前存在事务,则创建一个子事务作为当前事务的嵌套事务来运行,最终子事务和父事务一起提交;如果当前没有事务,则该取值等价于REQUIRED。

指定方法:通过使用propagation属性设置,例如: @Transactional(propagation = Propagation.REQUIRED)

5.3.事务回滚

5.4.超时设置

5.5.TransactionSynchronizationManager

https://blog.csdn.net/luzhensmart/article/details/89708881

TransactionSynchronizationManager:维护事务状态。在这个类中有一系列的ThreadLocal类型的类变量,它们就负责存储当前线程中的事务数据。

TransactionSynchronization接口定义了一系列的回调方法,对应一个事务执行的不同阶段:挂起、恢复、flush、提交(前、后)、完成(事务成功或失败)等。

@Transactional
public void save3() {
  // 1. 数据库操作
  // doSomething
  // 2. 判断是否开启了事务。  将操作注册到"afterCommit"阶段
  // 在事务提交之后,立即执行注册的方法。且这个方法时无法开启事务的
  if (TransactionSynchronizationManager.isSynchronizationActive()) {
      TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
          @Override
          public void afterCommit() {
              // 做些跟事务没有关系的事情 比如。发送mq
          }
      });
  }
}

5.6.TransactionAspectSupport

在开启的事务中,手动管理事务

@Transactional// 1.开启事务
@DataSourceSelector(value = DynamicDataSourceEnum.MASTER)
public void save() {
  User AA = new User(1L, "aa", "aa", "aa", 1, null, null);
  User BB = new User(2L, "bb", "bb", "bb", 1, null, null);
  // 数据库操作A
  this.userMapper.save(AA);
  
  Object savePoint = null;
  try {
      // 2. 手动设置一个事务的保存点
      savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
      // 数据库操作B
      this.userMapper.save(BB);
      // 3.手动回滚事务到保存点
      TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);
  } catch (Exception e) {
      e.printStackTrace();
  } 
  // 这个时候,数据库操作A成功,B回滚
}

@Transactional
@DataSourceSelector(value = DynamicDataSourceEnum.MASTER)
public void save2() {
  User AA = new User(1L, "aa", "aa", "aa", 1, null, null);
  User BB = new User(2L, "bb", "bb", "bb", 1, null, null);
  try {
      // 数据库操作A
      this.userMapper.save(AA);
      // 数据库操作B
      this.userMapper.save(BB);
      // 之前回滚全部事务
      TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
  } catch (Exception e) {
      e.printStackTrace();
  } 
  // 这个时候,上面的两个操作都失败的
}

文章信息

张雪随笔
master
Homepage
Issues
Download

This Software is under the terms of The Unlicense.