深入解析Spring事务原理,一文带你全面理解

2023年 12月 26日 70.0k 0

前言

在Spring中,事务管理主要通过AOP功能实现,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,Spring支持编程式事务管理和声明式事务管理两种方式。

  • 声明式事务
  • @Transactional
  • 编程式事务
  • TransactionTemplate

  • TransactionManager

四大特性

  • 原子性(Atomicity):一个事务中的所有操作,要么都完成,要么都不执行。对于一个事务来说,不可能只执行其中的一部分。
  • 一致性(Consistency):数据库总是从一个一致性的状态转换到另外一个一致性状态,事务前后数据的完整性必须保持一致。。
  • 隔离性(Isolation):一个事务所做的修改在最终提交以前,对其它事务是不可见的,多个事务之间的操作相互不影响。
  • 持久性(Durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

隔离级别

  • Read Uncommitted(读取未提交内容):一个事务可以看到其他事务已执行但是未提交的结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少,并且存在脏读问题。
  • Read Committed(读取已提交内容):一个事务只能看到其他事务已执行并已提交的结果(Oracle、SQL Server默认隔离级别)。这种隔离级别支持不可重复读,因为同一事务的其他实例在该实例处理期间可能会有新的commit,所以同一select可能返回不同结果。
  • Repeatable Read(可重读):同一事务的多个实例在并发读取数据时,会看到同样的数据行(MySQL的默认事务隔离级别)。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC)机制解决了不可重复读问题,存在幻读问题。
  • Serializable(可串行化):最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

隔离级别

脏读

不可重复读

幻读

Read Uncommitted

Read Committed

×

Repeatable Read

×

×

Serializable

×

×

×

传播级别

传播级别

含义

PROPAGATION_REQUIRED

支持当前事务,如果当前没有事务,则新建一个事务

PROPAGATION_SUPPORTS

支持当前事务,如果当前没有事务,则以非事务进行

PROPAGATION_MANDATORY

支持当前事务,如果当前没有事务,则抛异常

PROPAGATION_REQUIRES_NEW

新建事务,如果当前存在事务,则把当前事务挂起

PROPAGATION_NESTED

如果当前存在事务,则在嵌套事务内执行。如果没有,则进行与PROPAGATION_REQUIRED类似操作

PROPAGATION_NOT_SUPPORTED

以非事务进行,如果当前存在事务,则挂起事务,执行当前逻辑,结束后恢复上下文的事务

PROPAGATION_NEVER

以非事务进行,如果当前存在事务,则抛异常

案例

导入相关依赖

数据源、数据库驱动、spring-jdbc模块


    org.springframework
    spring-jdbc
    4.3.12.RELEASE


    mysql
    mysql-connector-java
    5.1.44


    com.alibaba
    druid-spring-boot-starter
    1.1.10

配置数据源

配置数据源、JdbcTemplate(Spring提供的简化数据库操作的工具)操作数据

@Bean
public DataSource dataSource(){
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setUsername("root");
    dataSource.setPassword("root");
    dataSource.setDriverClassName("com.mysql.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://localhost:3306/scp");
    return dataSource;
}

@Bean
public JdbcTemplate jdbcTemplate(){
    //Spring对@Configuration类会特殊处理;给容器中加组件的方法,多次调用都只是从容器中找组件
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
    return jdbcTemplate;
}

数据访问

@Repository
public class UserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    @Transactional
    public void insert(){
        String sql = "INSERT INTO user (name,age) VALUES(?,?)";
        String username = UUID.randomUUID().toString().substring(0, 5);
        jdbcTemplate.update(sql, username,19);
        int a = 1/0;
    }

}

开启事务,配置事务管理器

@EnableTransactionManagement  // 开启事务
@ComponentScan("org.yian")
@Configuration
public class TxConfig {
    //数据源
    @Bean
    public DataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/scp");
        return dataSource;
    }

    @Bean
    public JdbcTemplate jdbcTemplate(){
        //Spring对@Configuration类会特殊处理;给容器中加组件的方法,多次调用都只是从容器中找组件
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
        return jdbcTemplate;
    }

    //注册事务管理器在容器中
    @Bean
    public PlatformTransactionManager transactionManager(){
        return new DataSourceTransactionManager(dataSource());
    }
}

测试类

@Test
public void test01(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TxConfig.class);

    UserService userService = applicationContext.getBean(UserService.class);

    userService.insertUser();
    applicationContext.close();
}

原理

Spring 事务管理的实现原理主要涉及两个方面:事务管理器和代理机制:

  • 事务管理器(Transaction Manager):

Spring通过PlatformTransactionManager接口定义了事务管理器的标准。这个接口有多个实现,包括常用的DataSourceTransactionManager、JpaTransactionManager、HibernateTransactionManager等,每个都专门用于不同的持久化技术。

事务管理器的主要职责是开始、提交或回滚事务。当使用声明式事务管理时,开发者只需要配置相应的事务管理器,而不必亲自编写事务管理的代码

  • 代理机制:
  • Spring 通过代理机制为事务管理提供支持。它使用AOP来在方法调用前后添加额外的逻辑,即切面。在事务管理中,这个额外的逻辑包括开启、提交或回滚事务。

  • 当使用声明式事务管理时,Spring 会动态创建一个代理对象,该代理对象包装了目标对象(拥有业务逻辑的对象)。在方法调用时,代理对象会在执行前后添加事务管理的逻辑

@EnableTransactionManagement:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({TransactionManagementConfigurationSelector.class})
public @interface EnableTransactionManagement {
    boolean proxyTargetClass() default false;

    AdviceMode mode() default AdviceMode.PROXY;

    int order() default Integer.MAX_VALUE;
}

TransactionManagementConfigurationSelector:

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector {
    public TransactionManagementConfigurationSelector() {
    }

    protected String[] selectImports(AdviceMode adviceMode) {
        switch (adviceMode) {
            case PROXY:
                return new String[]{AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
            case ASPECTJ:
                return new String[]{this.determineTransactionAspectClass()};
            default:
                return null;
        }
    }

    private String determineTransactionAspectClass() {
        return ClassUtils.isPresent("javax.transaction.Transactional", this.getClass().getClassLoader()) ? "org.springframework.transaction.aspectj.AspectJJtaTransactionManagementConfiguration" : "org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration";
    }
}

EnableTransactionManagement 会利用 TransactionManagementConfigurationSelector 给容器中会导入两个组件 AutoProxyRegistrar、 ProxyTransactionManagementConfiguration

AutoProxyRegistrar:

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean candidateFound = false;
        Set annTypes = importingClassMetadata.getAnnotationTypes();
        Iterator var5 = annTypes.iterator();

        while(var5.hasNext()) {
            String annType = (String)var5.next();
            AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
            if (candidate != null) {
                Object mode = candidate.get("mode");
                Object proxyTargetClass = candidate.get("proxyTargetClass");
                if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() && Boolean.class == proxyTargetClass.getClass()) {
                    candidateFound = true;
                    if (mode == AdviceMode.PROXY) {
                        AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
                        if ((Boolean)proxyTargetClass) {
                            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                            return;
                        }
                    }
                }
            }
        }

AutoProxyRegistrar 给容器中注册一个 InfrastructureAdvisorAutoProxyCreator 组件,利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行调用

ProxyTransactionManagementConfiguration:

public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {
        BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
        advisor.setTransactionAttributeSource(transactionAttributeSource);
        advisor.setAdvice(transactionInterceptor);
        if (this.enableTx != null) {
            advisor.setOrder((Integer)this.enableTx.getNumber("order"));
        }

        return advisor;
    }

ProxyTransactionManagementConfiguration 给容器中注册事务增强器

public TransactionAttributeSource transactionAttributeSource() {
        return new AnnotationTransactionAttributeSource();
    }

事务增强器要用事务注解的信息,AnnotationTransactionAttributeSource解析事务注解

public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
        TransactionInterceptor interceptor = new TransactionInterceptor();
        interceptor.setTransactionAttributeSource(transactionAttributeSource);
        if (this.txManager != null) {
            interceptor.setTransactionManager(this.txManager);
        }

        return interceptor;
    }

事务拦截器TransactionInterceptor保存了事务属性信息,事务管理器,并且实现了 MethodInterceptor,在目标方法执行的时候执行拦截器链(事务拦截器)

TransactionAspectSupport:

protected Object invokeWithinTransaction(Method method, @Nullable Class targetClass, InvocationCallback invocation) throws Throwable {
        TransactionAttributeSource tas = this.getTransactionAttributeSource();
        TransactionAttribute txAttr = tas != null ? tas.getTransactionAttribute(method, targetClass) : null;
        TransactionManager tm = this.determineTransactionManager(txAttr);
        Object retVal;
        if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
            boolean isSuspendingFunction = KotlinDetector.isSuspendingFunction(method);
            boolean hasSuspendingFlowReturnType = isSuspendingFunction && "kotlinx.coroutines.flow.Flow".equals((new MethodParameter(method, -1)).getParameterType().getName());
            ReactiveTransactionSupport txSupport = (ReactiveTransactionSupport)this.transactionSupportCache.computeIfAbsent(method, (key) -> {
                Class reactiveType = isSuspendingFunction ? (hasSuspendingFlowReturnType ? Flux.class : Mono.class) : method.getReturnType();
                ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(reactiveType);
                if (adapter == null) {
                    throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " + method.getReturnType());
                } else {
                    return new ReactiveTransactionSupport(adapter);
                }
            });
            retVal = txSupport.invokeWithinTransaction(method, targetClass, invocation, txAttr, (ReactiveTransactionManager)tm);
            return isSuspendingFunction ? (hasSuspendingFlowReturnType ? TransactionAspectSupport.KotlinDelegate.asFlow((Publisher)retVal) : TransactionAspectSupport.KotlinDelegate.awaitSingleOrNull((Publisher)retVal, ((CoroutinesInvocationCallback)invocation).getContinuation())) : retVal;
        } else {
            PlatformTransactionManager ptm = this.asPlatformTransactionManager(tm);
            String joinpointIdentification = this.methodIdentification(method, targetClass, txAttr);
     
     .............................
     .............................
     .............................
  • 先获取事务相关的属性
  • 再获取PlatformTransactionManager,如果事先没有添加指定任何transactionmanger,最终会从容器中按照类型获取一个PlatformTransactionManager
  • 执行目标方法,如果异常,获取到事务管理器,利用事务管理回滚操作;如果正常,利用事务管理器,提交事务

相关文章

Oracle如何使用授予和撤销权限的语法和示例
Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
社区版oceanbase安装
Oracle 导出CSV工具-sqluldr2
ETL数据集成丨快速将MySQL数据迁移至Doris数据库

发布评论