spring5 事务
1.事务介绍
1.1 事务添加到javaEE的service
1.2 声明式事务和编程试
1.3 声明式
xml 事务开发
注解方式
1.4 在Spring中进行事务开发底层用的aop原理
1.5 Spring事务管理Api
2. 步骤
1 注解配置
2.1 开启事务管理器
2.2 开始事务注解
2.3 在service层中加入,可以加载类上面,也可以加在方法上面
<?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: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/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <context:component-scan base-package="com.demo"> </context:component-scan> <!-- 引入外部属性文件--> <context:property-placeholder location="classpath*:jdbc.properties"/> <!-- 数据库连接池--> <bean id="druid" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${prop.driverClassName}"></property> <property name="url" value="${prop.url}"/> <property name="username" value="${prop.username}"/> <property name="password" value="${prop.password}"/> </bean> <!-- jdbc模板对象--> <bean id="jdbc" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="druid"></property> </bean> <!-- 开启事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="druid"></property> </bean> <!-- 开启注解--> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven> </beans>
package com.demo.service; import com.demo.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service @Transactional public class UserService { @Autowired private UserDao userDao; public void account(){ userDao.addMoney(); int a = 10/0; userDao.reduceMoney(); } }
2 xml配置
- 配置通知
- 配置切入点,切面
<?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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="com.demo"> </context:component-scan> <!-- 引入外部属性文件--> <context:property-placeholder location="classpath*:jdbc.properties"/> <!-- 数据库连接池--> <bean id="druid" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${prop.driverClassName}"></property> <property name="url" value="${prop.url}"/> <property name="username" value="${prop.username}"/> <property name="password" value="${prop.password}"/> </bean> <!-- jdbc模板对象--> <bean id="jdbc" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="druid"></property> </bean> <!-- 开启事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="druid"></property> </bean> <!-- 配置通知--> <tx:advice id="txadvice"> <tx:attributes> <tx:method name="account" propagation="REQUIRED" isolation="REPEATABLE_READ" /> </tx:attributes> </tx:advice> <!-- 配置切入点和切面--> <aop:config> <aop:pointcut id="pt" expression="execution(* com.demo.service.UserService.*(..))"/> <aop:advisor advice-ref="txadvice" pointcut-ref="pt"></aop:advisor> </aop:config> </beans>
3 事务传播
REQUIRED(默认的传播机制):
- 如果当前没有事务,则新建事务
- 如果当前存在事务,则加入当前事务,合并成一个事务
REQUIRES_NEW:
- 新建事务,如果当前存在事务,则把当前事务挂起
NESTED
- 如果当前没有事务,则新建事务
- 如果当前存在事务,则创建一个当前事务的子事务(嵌套事务),子事务不能单独提交,只能和父事务一起提交。
REQUIRED
一个类的A方法调用另一个类的B方法。
假设在A方法存在一个当前事务,B方法的事务传播机制为REQUIRED,则B方法会合并到A方法的事务里执行。
A、B任意一个方法异常(默认是RuntimeException和Error)都会导致A、B的操作被回滚。
Spring事务管理器不会吞异常。
B异常后会抛给A,A如果没有catch这个异常,会继续向上抛。如果A catch住了,Spring事务管理器会替A向上抛一个UnexpectedRollbackException。总之,一旦A、B回滚,A的调用方一定能收到一个异常感知到回滚。
REQUIRES_NEW
一个类的A方法调用另一个类的B方法。
假设在A方法存在一个当前事务,B方法的事务传播机制为REQUIRES_NEW,则B方法会新建一个事务并把A所在的事务挂起。A事务等到B事务执行完后,恢复执行。
这种传播机制下,需要小心死锁问题。A事务被挂起了,如果B事务要加的锁被A占用了就会发生死锁。
- 如果B发生异常,B事务一定回滚,B的异常随后会抛给A,如果A catch住了这个异常,A不会回滚,否则A也会回滚。
- 如果A发生异常,则只会回滚A,不会回滚B。
NESTED
一个类的A方法调用另一个类的B方法。
假设在A方法存在一个当前事务,B方法的事务传播机制为NESTED,则B方法会作为A方法所在事务的一个子事务执行。
子事务的底层实现:B方法执行前会在A所在事务中创建一个savepoint,B异常后回滚到此savepoint。
- 如果B异常,B一定回滚,B的异常随后会抛给A,如果A catch住了这个异常,A不会回滚,否则A也会回滚。这种情况和REQUIRES_NEW一样。
- 如果A发生异常,则A、B都会回滚。