本篇文章只涉及spring事务的配置,不进行事务的介绍。

spring通过PlatformTransactionManager接口作为事务管理器来进行事务的管理,它本身并不进行事务的创建以及相关操作,它就相当于事务管理的容器,里面放的是事务。事务使用有编程式事务和声明式事务,现在一般情况下都是使用声明式事务。

声明式事务使用方法:

1、在配置的xml文件中使用AOP模式来进行事务声明,如下所示

<bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>    
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
        </tx:attributes>
    </tx:advice>
    <aop:config>
        <aop:pointcut id="interceptorPointCut"
                      expression="execution(* com.test.service.impl.*.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="interceptorPointCut" />
    </aop:config>

2、使用注解形式进行事务声明,如下所示:

第一步:在xml配置文件中加入如下配置

<!-- 启动声明式注解 -->
<tx:annotation-driven transaction-manager="transactionManager" />

或者在被@configuration注解注释的类上添加@EnableTransactionManagement注解

第二步:在需要添加事务的类或方法上添加@Transactional注解,注解可以设置value(用来设置数据源)、transactionManager(同value作用相同)、propagation(设置事务的传播行为,使用枚举Propagation来选择值,默认是REQUIRED)、isolation(设置事务的隔离机制,使用枚举Isolation来选择值,默认是底层数据库支持的事务的默认隔离机制)、timeout(设置事务的超时时间,可以使用TransactionDefinition接口提供的值,默认值为-1)、readOnly(布尔值,设置事务为只允许读,不进行修改操作,如果是只有读的操作可以设置为true,官方解释设置为true时,会在运行时进行相应的优化,但是并不能保证进行修改操作会失败)、rollbackFor(设置进行事务回滚的异常类类型)、rollbackForClassName(设置进行事务回滚的异常类类名)、noRollbackFor(设置不会进行事务回滚的异常类类型)、noRollbackForClassName(设置不会进行事务回滚的异常类类名)

即使没有设置rollbackFor、rollbackForClassName的值,spring框架在我们调用的方法发生运行时异常的时候会进行事务回滚操作。

3、利用AOP的around类型的advice进行事务声明,如下所示

@Aspect
@Component
public class TransactionalAop {

    @Autowired
    PlatformTransactionManager transactionManager;
 
    @Around("execution(* com.test.service.impl.*.*(..))")
    public void aroundMethod(ProceedingJoinPoint  pjp) throws Throwable {//        // 利用spring框架提供的事务模板类进行事务操作
//        TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
//        transactionTemplate.execute((status) -> {
//            Object result = null;
//                try {
//                    result = pjp.proceed();
//                } catch (Throwable e) {
//                    if (e instanceof RuntimeException) {
//                        throw (RuntimeException)e;
//                    } 
//                    if (e instanceof Error) {                        
//                        throw (Error)e;
//                    }
//                    throw new RuntimeException(e);
//                }
//                return result;
//        });
        
        // 手动创建事务(同时开启事务)并根据方法调用结果进行事务回滚或者提交
        TransactionStatus transaction = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
            pjp.proceed();
        } catch (Throwable e) {
            transactionManager.rollback(transaction);
            throw e;
        }
        transactionManager.commit(transaction);
    }
}

如上所示,我们可以利用spring提供的TransactionTemplate类来进行事务的相关操作,但是这里使用有个小缺陷,就是execute(TransactionCallback<T> action)方法里的参数action中的doInTransaction(TransactionStatus status)方法没有显式抛出异常,所以我们只能在doInTransaction(TransactionStatus status)方法中抛出运行时异常或Error来进行事务的回滚。如果是自己手动进行事务开启、提交、回滚就不会存在这个问题,如上所示,利用PlatformTransactionManager接口的getTransaction(@Nullable TransactionDefinition definition)方法来进行事务的开启,最后根据方法的执行结果来进行事务提交或回滚操作。

综合:

第一种方法是老式的xml配置文件进行事务配置的方法,需要用到xml配置文件,在如今springboot肆意横行的时代(springboot允许我们加载xml配置文件,但是如果还在使用xml配置文件不是又回去了吗?!),这种配置方式已经算是落伍了。

第二种方法是基于注解进行事务配置的方法,算是比较方便。优点是:事务管理的粒度比较细,@Transactional既可以用在类上也可以用在方法上;缺点:至少每个业务逻辑的类都要用到@Transactional注解。

第三种方法是利用aop的动态代理原理,在方法调用之前进行事务开启,根据方法执行结果来进行事务的相关操作。优点:事务统一管理,不在需要每个类或方法上添加@Transactional注解;缺点:事务管理的粒度比较粗。