Spring源码(八):Spring事务详解

一、事务执行流程

  1. 开启事务
  2. 执行DML语句
  3. 提交事务/回滚事务

说明:如果多个DML语句是同一个连接对象操作的,他们的算作同一个事务,前提是关闭自动提交。

二、Spring事务切面

        事务切面开启注解@EnableTransactionManagement,由注解上的@Import(TransactionManagementConfigurationSelector.class)来到selectImports方法。PROXY代表JDK事务,引入了AutoProxyRegistrar和ProxyTransactionManagementConfiguration。
在这里插入图片描述
        AutoProxyRegistrar的作用是注册aop的入口类InfrastructureAdvisorAutoProxyCreator,把属性设置到入口类中,最终会copy到proxyFactory中。Bean实例化最后一步判断是否有切面,如果有则生成代理。
在这里插入图片描述
        ProxyTransactionManagementConfiguration中定义了事务切面BeanFactoryTransactionAttributeSourceAdvisor。
        首先在事务切面里面设置处理事务属性对象TransactionAttributeSource,是由下面transactionAttributeSource方法创建的。这个是@Transactional注解的属性解析类,解析完注解后将属性封装成transactionAttribute对象。在transactionAttributeSource中可以看到它是AnnotationTransactionAttributeSource对象。
        enableTx是@EnableTransactionManagement中的排序属性,是一个map,根据这个属性可以排序切面。
在这里插入图片描述
        事务切面会设置advice,是TransactionInterceptor类型,由下面的transactionInterceptor定义,它继承了MethodInterceptor。
在这里插入图片描述
        在transactionInterceptor中可以看到如果txManager不为空,advice就设置了事务管理器,它是在父类AbstractTransactionManagementConfiguration中定义的。由setConfigurers可以依赖注入TransactionManagementConfigurer,在annotationDrivenTransactionManager方法中返回事务管理器,这就要求定义一个类实现TransactionManagementConfigurer,如下图所示。启动过程中没有txManager无影响,一般也不用这种方式,常用的会在后面说。
在这里插入图片描述
在这里插入图片描述

三、事务切面的Pointcut和Advice

        每个切面都会有Pointcut和Advice。首先看事务切面的PointCut,它定义在BeanFactoryTransactionAttributeSourceAdvisor中,在代理生成之前会调用ClassFilter的matches和MethodMatcher的matches。
在这里插入图片描述
        ClassFilter。TransactionAttributeSourcePointcut第一行就定义了ClassFilter为TransactionAttributeSourceClassFilter,但是这个
ClassFilter其实什么都没做。
在这里插入图片描述
        看到它的matches方法,关键代码是tas.isCandidateClass(clazz)。这里的TransactionAttributeSource为AnnotationTransactionAttributeSource,看到它的isCandidateClass。在这里插入图片描述
        isCandidateClass中循环调用了annotationParsers容器中的TransactionAnnotationParser.isCandidateClass。annotationParsers是在上面AnnotationTransactionAttributeSource中定义的,这里起作用的也就只有
SpringTransactionAnnotationParser。
在这里插入图片描述
        在SpringTransactionAnnotationParser的isCandidateClass中可以看到,它是在找java.开头的注解和java.开头的类。@Transactional注解是Spring定义的,事务相关的类是自定义的,所以都不符合,返回true。于是ClassFilter的matches一直都是返回true。
在这里插入图片描述
        MethodMatcher。TransactionAttributeSourcePointcut继承了StaticMethodMatcherPointcut,StaticMethodMatcherPointcut继承了StaticMethodMatcher,StaticMethodMatcher实现了MethodMatcher,所以TransactionAttributeSourcePointcut本身就是MethodMatcher,它里面定义了MethodMatcher的matches方法。
        matches方法会调用2次, 第一次是找切面的过程,代理对象生成之前会调用一次,第二次是在走拦截器链的时候又会被调用。ClassFilter的matches是一直返回为true的,也就是说只要方法上面能拿到@Transactional注解就生成代理。这里是拿事务属性,拿事务属性就相当于有@Transactional注解。
在这里插入图片描述
        看到AbstractFallbackTransactionAttributeSource的getTransactionAttribute,首先从缓存中取TransactionAttribute,如果不为空直接返回,如果为空,则需要在computeTransactionAttribute中得到TransactionAttribute。
在这里插入图片描述
        computeTransactionAttribute中首先判断方法是否为public,如果是非public方法则返回null不会生成代理。获取原始方法,AopUtils.getMostSpecificMethod(method, targetClass);很有用,它总能拿到原始方法(因为生成代理之后,一般拿到的都是方法的代理)。
在这里插入图片描述
        获取方法上面@Transactional注解的属性,点进去后来到AnnotationTransactionAttributeSource的determineTransactionAttribute,方法中主要是通过SpringTransactionAnnotationParser的parseTransactionAnnotation,拿到@Transactional的属性并封装成TransactionAttribute对象。其中,attributes相当于map,存的都是@Transactional的属性,parseTransactionAnnotation中拿到属性封装成对象。
在这里插入图片描述
        如果方法上没有注解,再通过findTransactionAttribute去类上找注解。点进去可以看到它也是来到AnnotationTransactionAttributeSource的determineTransactionAttribute,逻辑和上面从方法上找注解是一样的。
        再回到AbstractFallbackTransactionAttributeSource的getTransactionAttribute,如果TransactionAttribute不为空,将它放入缓存,然后返回。拿到了事务属性返回true,生成代理。

        Advice。事务切面的Advice即TransactionInterceptor,看到它的invoke方法,invocation::proceed很明显就是一个链式调用。
        进入invokeWithinTransaction。因为ProxyTransactionManagementConfiguration的transactionInterceptor中在依赖注入后将TransactionAttributeSource设置到advice中了,所以能通过getTransactionAttributeSource拿到事务属性类AnnotationTransactionAttributeSource,然后获取事务属性和事务管理器。
在这里插入图片描述
        determineTransactionManager中可以看到如果事务管理器为空,从Spring容器中拿到TransactionManager类型的类作为事务管理器。也就是说,只要自定义一个TransactionManager,注册到Spring容器中,就能引入事务管理器。下面白图中的DataSourceTransactionManager就是TransactionManager,一般都是用这种方式引入事务管理器。
在这里插入图片描述
在这里插入图片描述
        asPlatformTransactionManager是校验事务管理器,methodIdentification是获取方法名称。
在这里插入图片描述

四、注解事务的源码分析

        还是在invokeWithinTransaction方法中。
在这里插入图片描述
        进入createTransactionIfNecessary后,tm.getTransaction(txAttr);主要是开启事务。
在这里插入图片描述
        进入tm.getTransaction(txAttr);再进入doGetTransaction();
DataSourceTransactionObject是事务对象,里面有个newConnectionHolder标识,标识是否是新连接。事务肯定是跟连接对象挂钩的,父类JdbcTransactionObjectSupport中定义了当前连接对象的包装类ConnectionHolder,可以理解为数据库连接对象。JdbcTransactionObjectSupport还定义了连接对象属性相关的信息,包括隔离级别、是否只读、是否允许回滚。所以DataSourceTransactionObject是跟连接对象有关的一些包装。
在这里插入图片描述

        接着看doGetTransaction方法,txObject.setSavepointAllowed(isNestedTransactionAllowed());含义为是否创建回滚点,isNestedTransactionAllowed()默认返回的是true。
        obtainDataSource()是获取数据源对象,即上面例子中设置的数据源,TransactionSynchronizationManager.getResource是根据dataSource从ThreadLocal中获取到连接对象,第一次是没有值的,为null。ThreadLocal的结构是Map<DataSource, ConnectionHolder>
        拿到连接对象后放到事务对象中,设置newConnectionHolder为false。
在这里插入图片描述
        isExistingTransaction(transaction)是判断事务对象中的连接对象是否为空。第一次进来connectionHolder为空,所以if不会走,看到下面的代码。
在这里插入图片描述
        第一次进来会首先判断事务的传播属性,事务的传播属性是用来控制事务流转的。传播属性为REQUIRED、REQUIRES_NEW、NESTED执行下面的代码。传播属性默认为REQUIRED。
        首先将事务挂起,然后执行startTransaction。
在这里插入图片描述
        startTransaction中下面的代码是创建一个新的事务状态。newTransaction为true,这是核心。DefaultTransactionStatus对象主要是标识事务的一些属性,是否是最新事务,是否是旧的事务,Spring可以通过它来控制事务的传播。

DefaultTransactionStatus status = newTransactionStatus(
				definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);

        在newTransactionStatus方法中可以看到把事务对象、状态标识、是否挂起等属性都传到了DefaultTransactionStatus中返回。
在这里插入图片描述
        doBegin(transaction, definition);开启事务。首先判断事务对象中有没有连接对象,如果没有连接对象,从dataSource中拿到连接对象,包装成ConnectionHolder后设置到事务对象中,并把newConnectionHolder设置为true。
在这里插入图片描述
        设置有事务标识,设置是否只读连接和事务隔离级别到连接对象中,设置该连接的上一个隔离级别。如果是自动提交,关闭自动提交。
在这里插入图片描述
        把事务活跃状态设置为true,设置事务超时时间。首先判断newConnectionHolder是否为true,现在是为true的。TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());是绑定线程、dataSource、连接对象。在这里可以看到线程对应的是map,map中是数据源对应连接对象。在这里插入图片描述
        从doBegin出来后来到prepareSynchronization,这里是改变TransactionSynchronizationManager中的事务状态。DefaultTransactionStatus是Spring内部流转的事务状态,TransactionSynchronizationManager相当于开发人员使用的事务状态工具类,可以拿到事务的流转状态,是否已经提交、回滚。
在这里插入图片描述
        从startTransaction出来后来到prepareTransactionStatus。每走到一步事务状态都是不一样的,事务状态是实时在变的,这里设置newTransaction为true,最后返回DefaultTransactionStatus。
在这里插入图片描述
        最后从tm.getTransaction(txAttr);跳出,来到return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); 把事务管理器、事务属性、joinpoint和事务状态包装成TransactionInfo。
在这里插入图片描述
        从TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);跳出,retVal = invocation.proceedWithInvocation();调用被代理方法,进行链式调用。completeTransactionAfterThrowing(txInfo, ex);做事务回滚,commitTransactionAfterReturning(txInfo);做事务提交。
在这里插入图片描述
        进入commitTransactionAfterReturning(txInfo);,进入txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());的commit,processCommit(defStatus);执行事务提交。如果是required事务doBegin的时候isNewTransaction()为true。这时,提交事务。
在这里插入图片描述

五、Sping事务的传播属性

        Spring事务的传播属性包括REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER、NESTED。这里只在源码层面介绍REQUIRED、REQUIRES_NEW和NESTED。
        先看下面的代码,transaction上有@Transactional注解,方法中调用了areaService.addArea方法和goodsService.addGoods方法,这两个方法上面也都有@Transactional注解。

    @Transactional
    public void transaction(AreaInfo areaInfo, Good good) {
        ConnectionHolder resource = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);
        System.out.println(resource.getConnection());
        areaService.addArea(areaInfo);
        goodsService.addGoods(good);
    }
    @Transactional
    public int addArea(AreaInfo areaInfo) {
        ConnectionHolder resource = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);
        System.out.println(resource.getConnection());
        int i = commonMapper.addArea(areaInfo);
        return i;
    }
    @Transactional
    public int addGoods(Good good) {
        ConnectionHolder resource = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);
        System.out.println(resource.getConnection());
        int i = commonMapper.addGood(good);
        return i;
    }

        根据上面的代码和Spring源码可以整理出下面的伪代码。

transaction方法执行开始
try{
	// 开启事务
	createTransactionIfNecessary
		areaService.addArea(areaInfo);
		addArea方法执行开始
		try{
			// 开启事务
			createTransactionIfNecessary
			int i = commonMapper.addArea(areaInfo);
        	return i;
        } catch(Throwable ex) {
			// 事务回滚
			throw ex;
		}
		// 事务提交
		commitTransactionAfterReturning(txInfo);
		addArea方法执行结束
		
		goodsService.addGoods(good);
		addGoods方法执行开始
		try{
			// 开启事务
			createTransactionIfNecessary
			int i = commonMapper.addGood(areaInfo);
        	return i;
        } catch(Throwable ex) {
			// 事务回滚
			throw ex;
		}
		// 事务提交
		commitTransactionAfterReturning(txInfo);
		addGoods方法执行结束
} catch(Throwable ex) {
	// 事务回滚
	throw ex;
}
// 事务提交
commitTransactionAfterReturning(txInfo);
transaction方法执行完成
  1. REQUIRED传播属性
    含义:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
    意思是,整个项目只有一个事务,不管有多少次方法调用,就只有一个事务。自始至终,连接对象都是同一个。

        上面的测试代码先执行transaction方法的createTransactionIfNecessary开启事务,在doBegin中创建连接对象。
        接着执行addArea方法的createTransactionIfNecessary,来到AbstractPlatformTransactionManager的getTransaction方法。
        当前是有连接对象的,所以能进到handleExistingTransaction。
在这里插入图片描述
        REQUIRED传播属性不会走任何的if条件,到最后一行代码才返回。定义newTransaction为false,这是决定事务是否回滚、提交的关键。

return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);

        在事务回滚completeTransactionAfterThrowing中可以下面的代码,当newTransaction为true才会回滚。
在这里插入图片描述
        在事务提交中也可以看到newTransaction为true才会提交事务。
在这里插入图片描述
        所以,当传播属性为REQUIRED,areaService.addArea方法和goodsService.addGoods方法出现异常不会回滚事务只会往上抛异常,正常执行也不会提交事务。只在执行transaction方法的completeTransactionAfterThrowing或commitTransactionAfterReturning时做事务的统一回滚或提交。

思考问题一:
        在最外层用try…catch…捕获异常,只做打印的话,如果addArea或addGoods抛出异常还会事务回滚吗?

 	@Transactional
    public void transaction(AreaInfo areaInfo, Good good) {
        try {
            ConnectionHolder resource = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);
            System.out.println(resource.getConnection());
            areaService.addArea(areaInfo);
            goodsService.addGoods(good);
        } catch (Exception e){
            e.printStackTrace();
        }
    }

        来到事务回滚的代码中,当addArea或addGoods抛出异常,会设置事务对象中连接对象的rollbackOnly为true。
在这里插入图片描述
        因为我们捕获了异常,所以会执行transaction方法的commitTransactionAfterReturning做事务提交。由下面的代码可以看到,当连接对象的rollbackOnly为true时,会做事务回滚。
在这里插入图片描述
        在最外层捕获异常会做事务回滚。如果在areaService.addArea或goodsService.addGoods中捕获异常则不会做事务回滚。

  1. REQUIRES_NEW
    含义:新建事务,如果当前存在事务,把当前事务挂起。

        给addArea方法的传播属性定为REQUIRES_NEW。

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int addArea(AreaInfo areaInfo) {
        ConnectionHolder resource = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);
        System.out.println(resource.getConnection());
        int i = commonMapper.addArea(areaInfo);
        return i;
    }

        执行addArea方法的createTransactionIfNecessary,在handleExistingTransaction中先将前一个事务挂起。
在这里插入图片描述
        在suspend的doSuspend中,把连接对象设置为null,将前一个事务的连接对象和当前线程解除绑定。
在这里插入图片描述
        把包装了挂起连接对象的对象返回。
在这里插入图片描述
        SuspendedResourcesHolder suspendedResources = suspend(transaction);执行完后在startTransaction中开启事务,在startTransaction中把suspendedResources封装在事务状态中。
在这里插入图片描述
        addArea方法在processRollback中回滚事务或在processCommit中提交事务后,finally中处理挂起事务,恢复前面的连接。
在这里插入图片描述
        doCleanupAfterCompletion是释放当前连接。也是将线程和当前连接对象解绑的操作,并且设置自动提交为true。方法中还执行了DataSourceUtils.releaseConnection,这个里面执行了con.close();
        con一般为数据库连接池创建的代理对象,con.close();相当于将连接释放到连接池。
在这里插入图片描述
        在resume方法里面恢复绑定关系,doResume中将当前线程重新和挂起的transaction方法的连接对象绑定。伪代码中,在goodsService.addGoods方法执行createTransactionIfNecessary开启事务时,doGetTransaction()可以拿到transaction方法的连接对象,接着执行下面的代码。

  1. NESTED:
    含义:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

        将addArea和addGoods的传播属性都定为NESTED,addGoods中抛出异常。

	@Transactional(propagation = Propagation.NESTED)
    public int addArea(AreaInfo areaInfo) {
        ConnectionHolder resource = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
        System.out.println(resource.getConnection());
        int i = commonMapper.addArea(areaInfo);
        return i;
    }
	@Transactional(propagation = Propagation.NESTED)
    public int addGoods(Good good) {
        ConnectionHolder resource = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
        System.out.println(resource.getConnection());
        int i = commonMapper.addGood(good);
        throw new RuntimeException();
        return i;
    }

        看上面的伪代码,先来到transaction方法事务切面的createTransactionIfNecessary开启事务。接着是addArea方法的createTransactionIfNecessary,可以拿到连接对象,于是进入handleExistingTransaction,来到NESTED的if语句。
        useSavepointForNestedTransaction()默认返回true,可以嵌套事务。创建了事务状态,newTransaction为false。没有创建连接对象。在这里插入图片描述
        status.createAndHoldSavepoint();是拿到事务对象中的对接对象,创建回滚点。看到最后的实现,是利用Connection.setSavepoint()创建回滚点SAVEPOINT_1、SAVEPOINT_2…最后把回滚点设置到事务状态中。
在这里插入图片描述
        所以addArea方法或addGoods方法的createTransactionIfNecessary仅仅是在事务切面中创建了回滚点。
        执行addArea的被代理方法,最后执行addArea的事务提交,来到下面的代码。
在这里插入图片描述
        进入releaseHeldSavepoint,可以看到这里把addArea的回滚点抹掉了。连接对象执行了releaseSavepoint,事务状态的回滚点设为null。
在这里插入图片描述
        接着执行addGoods的createTransactionIfNecessary,同样也是创建了回滚点,执行被代理方法。但是addGoods抛出了异常,所以会来到completeTransactionAfterThrowing,走到下面的代码。
在这里插入图片描述
        进入status.rollbackToHeldSavepoint()的getSavepointManager().rollbackToSavepoint(savepoint)。
        根据回滚点回滚了sql,conHolder.resetRollbackOnly()是把连接对象的rollbackOnly置为了false。
在这里插入图片描述
        接着看伪代码,addGoods抛出异常后由transaction方法的事务切面捕获,来到下面的代码。
        transaction方法是没有回滚点的,且newTransaction属性为true,所以会执行doRollback(status); 执行全部回滚,也就是说会回滚addArea方法的sql。
        这里可以小小的总结一下。非第一次创建事务,并且传播属性是nested才有回滚点。如果是第一次创建事务,就算传播属性是nested也没有savepoint,因为没有连接对象。
在这里插入图片描述
思考问题二:
        如何保证addGoods抛出异常时addArea正常提交?
        我们看到,addGoods在做回滚的时候,会将连接对象的rollbackOnly设置为false。
        看一下提交事务commitTransactionAfterReturning对于该字段是怎么处理的,也就是下面的处理逻辑,当连接对象的rollbackOnly为true时回滚事务。连接对象的rollbackOnly为false会执行事务提交。
在这里插入图片描述
        这里的rollbackOnly属性为什么从事务对象中拿?因为涉及到多个事务切面传递参数,多个事务切面的事务对象不是同一个,但是连接对象绝对是同一个,可以看到最终是从连接对象中拿到rollbackOnly。
在这里插入图片描述
在这里插入图片描述
        回到问题,接对象的rollbackOnly为false,执行事务提交可以正常提交,所以在transaction方法中加try…catch…即可。

	@Transactional
    public void transaction(AreaInfo areaInfo, Good good) {
        try {
            ConnectionHolder resource = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
            System.out.println(resource.getConnection());
            areaService.addArea(areaInfo);
            goodsService.addGoods(good);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

六、Sping事务的异常校验

        在之前的源码中我们可以看到,Spring执行事务提交不一定会提交事务,那Spring执行事务回滚是否一定会回滚事务?
        在addGoods的@Transactional注解里加上属性rollbackFor = TransactionTestException.class,TransactionTestException继承RuntimeException。

public class TransactionTestException extends RuntimeException {
}
	@Transactional(propagation = Propagation.NESTED, rollbackFor = TransactionTestException.class)
    public int addGoods(Good good) {
        ConnectionHolder resource = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
        System.out.println(resource.getConnection());
        int i = commonMapper.addGood(good);
        throw new RuntimeException();
        return i;
    }

        进入事务回滚completeTransactionAfterThrowing的txInfo.transactionAttribute.rollbackOn(ex),最后来到RuleBasedTransactionAttribute.rollbackOn(Throwable ex)。
在这里插入图片描述
        rollbackRules是List,里面存的是TransactionTestException,抛出的异常ex为RuntimeException。
在这里插入图片描述
        进入判断方法getDepth。如果当前抛出的异常ex和rollbackFor定义的异常类名是一样的,返回depth,不然就一直找ex的父类去和rollbackFor定义的匹配,最后返回递归的深度depth。如果找到异常的最高级Throwable还不能匹配,就返回-1。
在这里插入图片描述
        看到rollbackOn方法,depth >= 0时winner才有值,当depth 返回-1时winner为nul,会执行super.rollbackOn(ex);
        来到DefaultTransactionAttribute的rollbackOn方法,判断ex是否为RuntimeException或Error。
在这里插入图片描述
        这里的例子抛出的是RuntimeException,所以会执行事务回滚。如果抛出的异常在getDepth和DefaultTransactionAttribute的rollbackOn方法中都不匹配,则会提交事务。

七、编程式事务

        注解@Transactional控制粒度太大,当有2个方法是不同的事务时,第2个方法执行时间过长,前面还有事务被挂起,这样连接对象就会浪费。可以用TransactionTemplate做编程式事务。
        TransactionTemplate中定义的传播属性是REQUIRED,所以不用管传播属性,每次调用都是单独的事务。
        创建TransactionTemplate,设置事务管理器。

	@Bean
    public PlatformTransactionManager annotationDrivenTransactionManager(DataSource dataSource) {
        DataSourceTransactionManager dtm = new DataSourceTransactionManager();
        dtm.setDataSource(dataSource);
        return dtm;
    }

    @Bean
    public TransactionTemplate transactionTemplate(PlatformTransactionManager platformTransactionManager) {
        TransactionTemplate transactionTemplate = new TransactionTemplate();
        transactionTemplate.setTransactionManager(platformTransactionManager);
        return transactionTemplate;
    }

        依赖注入使用TransactionTemplate。

	@Autowired
    private TransactionTemplate transactionTemplate;
    
	public int getTicketModeOne(Good good) {
        return transactionTemplate.execute(status -> {
            return commonMapper.addGood(good);
        });
    }

八、TransactionSynchronizationManager相关扩展

        我们可以在事务提交、回滚时做一些自己的业务。
        举个例子,事务提交后会执行下面的代码
在这里插入图片描述
        最后是循环调用TransactionSynchronization。在这里插入图片描述
        TransactionSynchronization是一个接口,里面定义的方法可以在事务任何环节做相关业务操作。但不是任何方法都是我们要关注的,这个时候可以用适配器模式,定义抽象类实现接口,然后用一个类继承抽象类,实现关注的方法。
        TransactionSynchronizationAdapter是Spring为我们定义的适配器。看下面的代码,我们可以用这个类在事务提交后做业务操作。

public class DoOnAfterCommit extends TransactionSynchronizationAdapter {
    @Override
    public void afterCommit() {
        super.afterCommit();
        System.out.println("=========事务提交后做事情==========");
    }
}
	@Transactional()
    public List<ConsultConfigArea> queryAreaFromDB(Map param) {
		// 注册触发器
        TransactionSynchronizationManager.registerSynchronization(new DoOnAfterCommit());
        logger.info("================从mysql里面查询数据 事务1========================");
        List<ConsultConfigArea> areas = commonMapper.queryAreaByAreaCode(param);
        return areas;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值