spring boot对事务的支持及处理过程分析

基础知识1:java事务

Java事务的类型有三种:JDBC事务、JTA(Java Transaction API)事务、容器事务。

1、JDBC事务

JDBC 事务是用 Connection 对象控制的。JDBC Connection 接口( java.sql.Connection )提供了两种事务模式:自动提交和手工提交。 java.sql.Connection 提供了以下控制事务的方法:

public void setAutoCommit(boolean)

public boolean getAutoCommit()

public void commit()

public void rollback()

使用 JDBC 事务界定时,您可以将多个 SQL 语句结合到一个事务中。JDBC 事务的一个缺点是事务的范围局限于一个数据库连接。一个 JDBC 事务不能跨越多个数据库。

 

三种Java事务差异

 

JDBC事务控制的局限性在一个数据库连接内,但是其使用简单。

JTA事务的功能强大,事务可以跨越多个数据库或多个DAO,使用也比较复杂。

容器事务,主要指的是J2EE应用服务器提供的事务管理,局限于EJB应用使用。

基础知识2:两个注解

spring boot对事务的支持主要是两个注解:

@EnableTransactionManagement

会自动扫注解 @Transactional 的方法和类(表示该类中所有public的方法都需要开启事务的)

在spring boot中这个注解会在系统启动时自动加载,具体过程见下面的加载过程解析

@Transactional

使用注解来选择需要使用事务的方法(切入点),声明该方法需要事务支持,这是一个基于 AOP 的实现操作

@Transactional来修饰一个方法时,将mysql的底层事务进行了抽象,总结起来就做了3件事:

1. 将autocommit=off,为回滚做准备

2. 根据配置的isoliation,set tx_isolation,默认是RC级别,所以即便你的mysql的tx_isolation是RR级别,但通过spring transaction会重新制定tx_isolation,优先级要高于mysql设置的tx_isolation

3. 在方法执行过程中如果报错,执行rollback,否则执行commit

spring boot 支持事务功能的加载过程解析

在spring boot中,在spring.factories中默认加载了

org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\

org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
@AutoConfigureAfter({ JtaAutoConfiguration.class, JpaBaseConfiguration.class,

      DataSourceTransactionManagerAutoConfiguration.class })

public class TransactionAutoConfiguration {

在TransactionAutoConfiguration加载之前要加载

JpaBaseConfiguration以及DataSourceTransactionManagerAutoConfiguration类

 

spring boot启动的时候,执行application.run方法后,执行到JpaBaseConfiguration类

执行到JpaBaseConfiguration类

@EnableConfigurationProperties(JpaProperties.class)
@Import(DataSourceInitializedPublisher.Registrar.class)
public abstract class JpaBaseConfiguration implements BeanFactoryAware {

@Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class)
public PlatformTransactionManager transactionManager() {
   return new JpaTransactionManager();
}

JpaProperties类

@ConfigurationProperties(prefix = "spring.jpa")

public class JpaProperties {

这也是我们在application.properties中设置所能生效的原因

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

spring.jpa.properties.hibernate.format_sql=true

spring.jpa.properties.hibernate.type=trace

在这里初始化transactionManager,即使用JPA作为数据库访问技术时,spring boot使用的是JpaTransactionManager,而使用JDBC作为数据库访问技术时,spring boot使用的是DataSourceTransactionManager

spring boot对事务的支持及处理过程分析_第1张图片

这里就初始化了transactionManager

之后会执行到ConfigurationClassPostProcessor的

@Override

public Object postProcessBeforeInitialization(Object bean, String beanName)  {

   if (bean instanceof ImportAware) {

      ImportRegistry importRegistry = this.beanFactory.getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class);

      AnnotationMetadata importingClass = importRegistry.getImportingClassFor(bean.getClass().getSuperclass().getName());

      if (importingClass != null) {

         ((ImportAware) bean).setImportMetadata(importingClass);

      }

   }

   return bean;

}

其中关键代码

((ImportAware) bean).setImportMetadata(importingClass);

 

在AbstractTransactionManagementConfiguration implement ImportAware中

@Override

public void setImportMetadata(AnnotationMetadata importMetadata) {

   this.enableTx = AnnotationAttributes.fromMap(

         importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName(), false));

   if (this.enableTx == null) {

      throw new IllegalArgumentException(

            "@EnableTransactionManagement is not present on importing class " + importMetadata.getClassName());

   }

}

加载了EnableTransactionManagement注解(即在spring boot中已经自动加载了@EnableTransactionManagement注解

 

AdviceMode mode() default AdviceMode.PROXY;

mode标识表示采用哪种事务通知,默认JDK proxy_based,另外一种是AspectJ weaving-based

public enum AdviceMode {

   PROXY,

   ASPECTJ

}

可以看到

spring boot对事务的支持及处理过程分析_第2张图片

 

其在EnableAsync,EnableCaching中都有使用,说明异步以及缓存功能与事务功能一样,都是基于jdk proxy_based 拦截器实现的

@EnableTransactionManager与@Transactional配合使用

org.springframework.transaction.interceptor.TransactionAttribute

org.springframework.transaction.interceptor.DefaultTransactionAttribute

org.springframework.transaction.interceptor.RuleBasedTransactionAttribute

 

@EnableAsync与@Async配合使用,使用

AnnotationAsyncExecutionInterceptor

AsyncAnnotationAdvisor

 

@EnableCaching与@Cacheable配合使用

org.springframework.cache.aspectj.AspectJCachingConfiguration

之后加载到DataSourceTransactionManagerAutoConfiguration中,初始化datasource

 

再之后终于加载到TransactionAutoConfiguration(autoconfigure包下的spring.factories中配置的事务管理配置类)

看到初始化后的transactionManager和datasource

spring boot对事务的支持及处理过程分析_第3张图片

加载流程图:

spring boot对事务的支持及处理过程分析_第4张图片

SimpleJpaRepository源码

@Repository

@Transactional(readOnly = true)

public class SimpleJpaRepository

      implements JpaRepository, JpaSpecificationExecutor {
@Transactional

public void delete(T entity) {

 

Spring Data JPA 对所有的默认方法都开启了事务支持,且查询类事务默认启用 readOnly=true 属性,其增删改的每个方法都使用@Transactional来覆盖readOnly属性的

@Transactional

spring boot对事务的支持及处理过程分析_第5张图片

@Transactional注解常用属性

spring boot对事务的支持及处理过程分析_第6张图片

 7个传播行为propagation

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

spring boot对事务的支持及处理过程分析_第7张图片

1. REQUIERD:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务

2. SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行

3. MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常

4. REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起

5. NOT_SUPPORTED:以非事务方式运行,如果存在当前事务,则把当前事务挂起

6. NEVER:以非事务方式运行,如果存在当前事务,则抛出异常

7. NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行,如果当前没有事务,则该取值等价于REQUIRED

 4个隔离级别isolation

Isolation isolation() default Isolation.DEFAULT;

spring boot对事务的支持及处理过程分析_第8张图片

1. DEFAULT:表示使用底层数据库的默认隔离级别。对大部分数据库而言,READ_COMMITED

2. READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别

3. READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值

4. REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读

5. SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下不会用到该级别

使用事务注意

1. spring boot这种默认机制,只需要在我们用事务时,在方法或者此方法的类上加上@Transactional注解即可。在实际工作中,我们一般都要在Service层的某些方法上加事务

@Transactional(rollbackOn= Exception.class)

public void saveUserInfo() throws Exception{

  //此方法有多个repository的调用

  userCustomerRepository.save(…);

  userRepository.save(…);

  //模拟异常,事务回滚

  throw New Exception(“test”);

}

 

2. @Transactional只能被应用到public方法上,对于其他非public的方法,如果标记了也不会出错,但方法没有事务功能

3. 用spring事务管理器,由spring来负责数据库的打开、提交、回滚。默认遇到运行期异常(throw new RuntimeException)会回滚;而遇到需要捕获的异常(throw new Exception)不会回滚。

要想所有异常都回滚,要加上

@Transactional(rollbackFor={Exception.class,其他异常})

如果让运行期异常不回滚,要加上

@Transactional(notRollbackFor=RuntimeException.class)

 

4. spring的建议是你在具体的类(或类的方法上)使用@Transactional注解,而不要使用在类所要实现的任何接口上。原因就是事务是基于代理的,所以以下两种情况即便添加了@Transactional注解也是无事务支持的

1)@Transactional注解使用在接口上,但是事务创建的是基于类的代理,那么事务的设置将不能被基于类的代理所识别,对象也将不会被事务代理所包装

2)如spring cache中的情况,当调用标识有@Cacheable的方法与@Cacheable标识的方法在同一个类中时,spring cache无效。原因也是@Cacheable会创建基于类的代理,但由于是同一个类的方法调用,所以不会被拦截到,所以缓存无效。

 

5. 事务有两种配置方式,一种是使用@Transactional显式的注解式事务。

还有一种是隐式事务,ASPECTJ的思路配置方法,所以不是没有加@Transactional注解就一定没有事务

参考:spring中使用aspectj为service package下的所有增删改方法添加事务

你可能感兴趣的:(spring事务,spring,boot)