编程式事务
使用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>
<!-- 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没有通过索引来更新数据,会导致行锁(通过索引上的索引项加锁实现行级锁)升级为表锁。使得一直占用此表,不释放。