编程式事务

使用transactionTemplate模板实现

在applicationContext.xml配置事务管理器transactionManager、事务管理模板transactionTemplate
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

<!--    配置扫描包-->
    <context:component-scan base-package="com.example.demo"></context:component-scan>

<!--    引入配置文件的数据源-->
    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:application.properties"></property>
    </bean>

<!--    配置数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${spring.datasource.driver-class-name}" />
        <property name="url" value="${spring.datasource.url}" />
        <property name="username" value="${spring.datasource.username}" />
        <property name="password" value="${spring.datasource.password}" />
    </bean>

<!--    Mybaits事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

<!--    编程式事务-->

<!--    事务管理模板-->
    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="transactionManager"></property>
    </bean>
</beans>

在service层注入transactionTemplate
 @Autowired
    private TransactionTemplate transactionTemplate;

//    编程式事务
    public void transferMoneyChange(){
        transactionTemplate.execute(transactionStatus -> {
            // 两个事务
            indexMapper.downMoney();
                 //  中间截止
            int n = 1 / 0;
            indexMapper.upMoney();
            return null;
        });
    }


声明式事务

applicationContext.xml里面配置AOP(切面和切入点)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

<!--    配置扫描包-->
    <context:component-scan base-package="com.example.demo"></context:component-scan>

<!--    引入配置文件的数据源-->
    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath: application.properties"></property>
    </bean>

<!--    配置数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${spring.datasource.driver-class-name}" />
        <property name="url" value="${spring.datasource.url}" />
        <property name="username" value="${spring.datasource.username}" />
        <property name="password" value="${spring.datasource.password}" />
    </bean>

<!--    Mybaits事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

<!--    基于aspectj声明式事务的配置-->

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
<!--            以transfer方法开头的添加事务-->
            <tx:method name="transfer*" isolation="DEFAULT" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

<!--    aop配置定义切面和切面点-->
    <aop:config>
<!--        定义切面点:那些类需要进行事务管理-->
        <aop:pointcut expression="execution(* com.example.demo.service.*.*(..)) "
                      id="transactionPointcut" ></aop:pointcut>
<!--        定义切面-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="transactionPointcut"></aop:advisor>
    </aop:config>
</beans>

都需要在pom.xml里面引入aspectj
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.7</version>
            <scope>runtime</scope>
        </dependency>


springBoot

不用加这些配置、直接在需要事务的方法上面加上@Transactional即可实现
  @Transactional
    public void transferMoneyChange() {

//        两个事务
        indexMapper.downMoney();

//        中间截止
        int n = 1 / 0;

        indexMapper.upMoney();
    }


Spring传播特性

传播(propagation)
介绍:当一个事务方法被另一个事务方法调用时,这个事务应该如何处理

7种事务隔离级别:

举例:在userService类里面写、A方法调用B方法
@Autowired UserService userService;

@Transactional
public void A(){
    userService.B();
}

@Transactional
public void B(){
    
}

propagation_never

没有就非事务正常执行,有事务就抛出异常。
也就是:A无事务,B正常执行。A有事务,B抛出异常
 @Transactional(propagation = Propagation.REQUIRED)
    public void A(){
        indexMapper.downMoney();
        indexService.B();
    }

    @Transactional(propagation = Propagation.NEVER)
    public void B(){
        indexMapper.upMoney();
    }
抛出异常:
org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'


propagation_not_supported

没有就非事务执行,有就直接挂起,然后非事务执行。
也就是说:A无事务,B正常执行。A有事务,B将A事务挂起,然后正常执行。
 @Transactional(propagation = Propagation.REQUIRED)
    public void A(){
        indexMapper.downMoney();
        indexService.B();
    }

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void B(){
        indexMapper.upMoney();
    }


propagation_supports

没有就非事务执行,有就使用当前事务。
也就是说:A无事务,B正常执行。A有事务,B执行A的事务。换句话来讲就是A、B任何一个异常一起回滚。
@Transactional(propagation = Propagation.REQUIRED)
    public void A(){
        indexMapper.downMoney();
        indexService.B();
        //A有异常、AB全部回滚
        int n = 1 / 0;
    }

    @Transactional(propagation = Propagation.SUPPORTS)
    public void B(){
        indexMapper.upMoney();
        //B有异常、AB全部回滚
        int n = 1 / 0;
    }


propagation_requires_new

有无都新建事务,原来有就将原事务挂起。
也就是说:A无事务,B新建事务执行,A有事务,B将原事务挂起,再执行B的事务。
换句话说:完全新建、B事务与A事务互不影响。A事务回滚、B事务不回滚。B事务回滚,A事务不回滚
 @Transactional(propagation = Propagation.REQUIRED)
    public void A(){
        indexMapper.downMoney();
        indexService.B();
                //  A有异常、A回滚
        int n = 1 / 0;
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void B(){
        indexMapper.upMoney();
                //  B有异常、B回滚
            int n = 1 / 0;     
        }


propagation_nested

没有就新建事务,有在当前事务中嵌套其他事务。
也就是说:A无事务,B新建事务执行。A 有事务,B新建事务绑定A的事务。
换句话来说:A事务回滚、B事务跟着回滚。B事务回滚,A事务不回滚。(大影响小、小不影响大)
@Transactional(propagation = Propagation.REQUIRED)
    public void A(){
        indexMapper.downMoney();
        indexService.B();
        //A异常、AB一起回滚
        int n = 1 / 0;
    }

    @Transactional(propagation = Propagation.NESTED)
    public void B(){
        indexMapper.upMoney();
        //B异常,B回滚、A事务不回滚
        int n = 1 / 0;
    }


propagation_required

默认的事务隔离级别。没有就新建事务,有就加入当前事务。
也就是说:A无事务,B新建事务。A有事务,B直接使用B的事务。
换句话来说:A、B事务异常,AB一起回滚。
@Transactional(propagation = Propagation.REQUIRED)
    public void A(){
        indexMapper.downMoney();
        indexService.B();
        //A有异常、AB全部回滚
        int n = 1 / 0;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void B(){
        indexMapper.upMoney();
        //B有异常、AB全部回滚
        int n = 1 / 0;
    }


propagation_mandatory

无事务抛出异常、有事务使用当前事务。
也就是说:A无事务,B抛出异常。A有事务,B直接使用A的事务。
    public void A(){
        indexMapper.downMoney();
        indexService.B();
    }

    @Transactional(propagation = Propagation.MANDATORY)
    public void B(){
        indexMapper.upMoney();
    }


注意点

  • 嵌套调用的时候一定是userService.B();将其交给IOC容器,使其暴露出来。如果直接this.B()调用会导致AOP失效、事务失效。
  • propagation_required_new、propagation_not_supported涉及到将原事务挂起,可能会导致死锁。因为如果SQL语句里面update没有通过索引来更新数据,会导致行锁(通过索引上的索引项加锁实现行级锁)升级为表锁。使得一直占用此表,不释放。