spring事务实现原理

spring事务有编程式事务和声明式事务两种实现方式。编程式事务是通过编写代码来管理事务的提交、回滚、以及事务的边界。这意味着开发者需要在代码中显式地调用事务的开始、提交和回滚。声明式事务是通过配置来管理事务,您可以使用注解或XML配置来定义事务的边界和属性,而无需显式编写事务管理的代码。

  • 编程式事务管理
    编程式事务管理允许开发人员通过代码编程的方式控制事务的处理逻辑,即开始、提交和回滚。这种方式通常用于需要更细粒度控制事务的应用程序中,可以更灵活精准地自主控制事务。在 Spring 框架中,编程式事务管理是通过 TransactionTemplate 或 PlatformTransactionManager 接口实现的。

  • 声明式事务管理
    声明式事务管理是 Spring 中最常用的事务管理方式,因为它能够以非侵入的方式集成到现有的应用程序中,减少代码量并提高可维护性。

事务隔离级别 (Isolation Levels)

事务隔离级别定义了事务之间是如何隔离的,Spring 支持四种不同的隔离级别:

ISOLATION_DEFAULT: 数据库默认的隔离级别。
ISOLATION_READ_UNCOMMITTED: 事务可以读取未提交的数据。
ISOLATION_READ_COMMITTED: 事务只能读取已提交的数据。
ISOLATION_REPEATABLE_READ: 对同一字段的多次读取将返回第一次读取的结果。
ISOLATION_SERIALIZABLE: 最严格的隔离级别,确保事务顺序执行。

@Transactional(isolation = Isolation.REPEATABLE_READ)
public void repeatableReadMethod() {
    // ...
}

附mysql的隔离级别:https://doctording.blog.csdn.net/article/details/80270741

编程式事务实现例子代码

如下为一种写法,事务的提交和会滚自己控制,借助spring的PlatformTransactionManager

// transactionManager是某一个具体的PlatformTransactionManager实现类的对象
private PlatformTransactionManager transactionManager;


// 定义事务属性
DefaultTransactionDefinition def = new DefaultTransactionDefinition();

// 获取事务
TransactionStatus status = transactionManager.getTransaction(def);

try {
    // 执行数据库操作
    // ...
    
    // 提交事务
    transactionManager.commit(status);
} catch (Exception ex) {
    // 回滚事务
    transactionManager.rollback(status);
}
    @Autowired
    private PlatformTransactionManager platformTransactionManager;

    @Autowired
    private JdbcTemplate jdbcTemplate;


    @PutMapping(value = "/test/transaction")
    public Boolean testTransaction() throws Exception {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus transactionStatus = platformTransactionManager.getTransaction(def);
        try {
            // 处理业务
            jdbcTemplate.update("insert into tb_user (name, phone, address) values (?, ?, ?)", "jack", "12345", "city");
            int c = 1 / 0;
            // 提交事务
            System.out.println("transaction commit...");
            platformTransactionManager.commit(transactionStatus);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            // 事务回滚
            System.out.println("transaction rollback..." + transactionStatus.isCompleted());
            platformTransactionManager.rollback(transactionStatus);
            return false;
        }
    }

PlatformTransactionManager的封装

PlatformTransactionManager是Spring事务管理的核心接口,通过 PlatformTransactionManager 接口设计了一系列与事务处理息息相关的接口方法,如 getTransaction()、commit()、rollback() 这些和事务处理相关的统一接口。对于这些接口的实现,很大一部分是由 AbstractTransactionManager 抽象类来完成的。

AbstractPlatformManager 封装了 Spring 事务处理中通用的处理部分,比如事务的创建、提交、回滚,事务状态和信息的处理,与线程的绑定等,有了这些通用处理的支持,对于具体的事务管理器而言,它们只需要处理和具体数据源相关的组件设置就可以了,比如在DataSourceTransactionManager中,就只需要配置好和DataSource事务处理相关的接口以及相关的设置。

@Transactional 普通例子代码和测试输出

@Transactional(rollbackFor = Exception.class)
@PutMapping(value = "/test/oom")
public Boolean testOom() throws Exception {
    TbUser tbUser = new TbUser();
    tbUser.setUserid("abc");
    tbUser.setPassword("123456");
    tbUser.setName("abc");
    tbUser.setPhone("abc");
    tbUser.setAddress("abc");

    try {
        List<TbUser> tbUserList = tbUserService.getAllUser();
        System.out.println("users size1:" + tbUserList.size());
        tbUserService.insert(tbUser);
        tbUserList = tbUserService.getAllUser();
        System.out.println("users size2:" + tbUserList.size());
        // 继续业务处理,模拟异常
        int c = 1 / 0;
        return true;
    } catch (Exception e) {
        e.printStackTrace();
        throw e;
    } finally {
        System.out.println("finally delete");
    }
}

测试输出如下
在这里插入图片描述
数据库去查询确实会滚了

@Transactional失效场景

没有被代理到

  • 当前类没有被Spring管理
  • final, static修饰的
  • 方法内部进行调用的

框架不支持或者配置错误

  • 非public方法
  • 多线程调用,本质是不在一个事务中
  • 未开启事务,如注解没有配置

错误使用@Transactional

  • 错误的传播机制:如不支持事务的传播机制为:PROPAGATION_SUPPORTS,PROPAGATION_NOT_SUPPORTED,PROPAGATION_NEVER,配置了,则在发生异常的时候,事务是不会回滚的。
  • rollbackFor属性设置错误:默认情况下事务仅回滚运行时异常和Error,不回滚受检异常(例如IOException);可以通过指定@Transactional(rollbackFor = Exception.class)的方式进行全异常捕获
  • 异常被内部catch,没有抛出

@Transactional注解原理:TransactionInterceptor

@EnableTransactionManagement注解会开启spring自动管理事务的功能,有了这个注解之后,spring容器启动的过程中,会拦截所有bean的创建过程,判断bean 是否需要让spring来管理事务,即判断bean中是否有@Transaction注解,判断规则如下

1、一直沿着当前bean的类向上找,先从当前类中,然后父类、父类的父类,当前类的接口、接口父接口,父接口的父接口,一直向上找,一下这些类型上面是否有 @Transaction注解

2、类的任意public方法上面是否有@Transaction注解

如果bean满足上面任意一个规则,就会被spring容器通过aop的方式创建代理,代理中会添加一个拦截器
org.springframework.transaction.interceptor.TransactionInterceptor
,它会拦截@Trasaction方法的执行,在方法执行前后添加事务的功能

@EnableTransactionManagement@Import({TransactionManagementConfigurationSelector.class})注册了AutoProxyRegistrarProxyTransactionManagementConfiguration两个bean

  • TransactionInterceptor代理实现
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
    public TransactionInterceptor() {
    }

    public TransactionInterceptor(PlatformTransactionManager ptm, Properties attributes) {
        this.setTransactionManager(ptm);
        this.setTransactionAttributes(attributes);
    }

    public TransactionInterceptor(PlatformTransactionManager ptm, TransactionAttributeSource tas) {
        this.setTransactionManager(ptm);
        this.setTransactionAttributeSource(tas);
    }

    @Nullable
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Class<?> targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null;
        Method var10001 = invocation.getMethod();
        invocation.getClass();
        return this.invokeWithinTransaction(var10001, targetClass, invocation::proceed);
    }

事务代理实现和TransactionAspectSupport重要类

直接跟随上方异常栈去走debug代码

org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction中,创建一个事务

TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

org.springframework.transaction.interceptor.TransactionAspectSupport#completeTransactionAfterThrowing进行了事务回滚
在这里插入图片描述

/**
 * Handle a throwable, completing the transaction.
 * We may commit or roll back, depending on the configuration.
 * @param txInfo information about the current transaction
 * @param ex throwable encountered
 */
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
	if (txInfo != null && txInfo.getTransactionStatus() != null) {
		if (logger.isTraceEnabled()) {
			logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
					"] after exception: " + ex);
		}
		if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
			try {
				txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
			}
			catch (TransactionSystemException ex2) {
				logger.error("Application exception overridden by rollback exception", ex);
				ex2.initApplicationException(ex);
				throw ex2;
			}
			catch (RuntimeException | Error ex2) {
				logger.error("Application exception overridden by rollback exception", ex);
				throw ex2;
			}
		}
		else {
			// We don't roll back on this exception.
			// Will still roll back if TransactionStatus.isRollbackOnly() is true.
			try {
				txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
			}
			catch (TransactionSystemException ex2) {
				logger.error("Application exception overridden by commit exception", ex);
				ex2.initApplicationException(ex);
				throw ex2;
			}
			catch (RuntimeException | Error ex2) {
				logger.error("Application exception overridden by commit exception", ex);
				throw ex2;
			}
		}
	}
}

在这里插入图片描述

debug到这里,然后Evaluate可以获取到两条记录
在这里插入图片描述

回滚完成后就只有一条记录:
在这里插入图片描述

另外可以看到事务的配置信息

  1. Spring事务传播方式是PROPAGATION_REQUIRED‌,即如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这也是最常见的选择。
  2. 隔离级别,使用数据库默认的(mysql默认隔离级别是REPEATABLE READ可重复读)
    在这里插入图片描述

回滚规则属性中也能看到设定的回滚规则:
在这里插入图片描述

复习Spring的事务传播机制有哪些

完全背不下来,直接理解记忆。

考虑如下代码的doLogic方法

class AService{

	@Autowired
	priavte BService bService;

	public void doLogic(){
		aaa();
		bService.bbb();
		ccc()
	}
}

可以看成如下,如果有事务,则是 有嵌套的,但是到底要不要,则可以自由决定

begin
	aaa()
    --------------
		begin
			bbb()
		commit();
    --------------
	ccc()
commit();

比如:bService里面可以

  1. 不要事务
  2. 要事务且用aService的事务(如果有)
  3. 要事务自己开启全新事务(这样里面就有三种不同的处理方式了)

对于aService:

  1. 本身没有事务
  2. 有事务, 如果有异常如何会滚:回滚自己的,还是把bService一起滚掉等等场景

这么一结合确实场景很多,Spring给出了如下的7种:
分别是:REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER和NESTED。

REQUIRED‌:如果当前存在事务,则加入该事务;如果不存在事务,则创建一个新的事务。这是Spring的默认传播机制,适用于大多数业务场景,确保多个事务操作在同一个事务中执行,以保持数据一致性‌

‌SUPPORTS‌:如果当前存在事务,则加入该事务;如果不存在事务,则以非事务方式执行。适用于那些不需要事务也能正确执行,但在事务环境下运行也不会造成任何问题的方法‌

‌MANDATORY‌:必须在一个已存在的事务中执行,如果当前没有事务则抛出异常。适用于那些必须在事务中执行的操作‌

‌REQUIRES_NEW‌:总是创建一个新的事务,如果当前存在事务,则将当前事务挂起,新的事务结束后,再恢复之前被挂起的事务。适用于那些需要在独立事务中执行的操作‌

‌NOT_SUPPORTED‌:不使用事务,如果当前存在事务,则将其挂起,直到该方法完成后再继续执行。适用于那些不需要事务的方法‌

‌NEVER‌:不允许在事务中执行,如果当前存在事务,则抛出异常。适用于那些必须在非事务环境中执行的操作‌


‌NESTED‌:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则新建一个事务。适用于需要嵌套事务的场景‌

实际工作中用到的事务处理

  1. dbOperate做了一系列数据库操作,必须在此之后进行callBack方法
method(){
	dbOpeate()
	TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization(){
 		@Override
        public void afterCompletion(int status) {
           callBack();
        }
    });
}
  1. 新事务处理完成后,再处理业务逻辑
@Transactional(rollbackFor = Exception.class)
void method(){
	try
		bService.getLock();
		cService.dbOperation();
	}finally{
		bService.releaseLock();
	}
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
void getLock(){

}

@Transactional(propagation = Propagation.REQUIRES_NEW)
void releaseLock(){

}

执行getLock和releaseLock是挂起method,自己开启新事务快速处理完成;因为数据库操作简单且快,不至于和后面的dbOperation产生模糊关系。

  1. 使用事务事件监听

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)

一些列复杂操作,在事务前后有特定的操作,如统计、告知等

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐