目录
spring提供的2种事务管理方式
- 编程式事务:使用TransactionTemplate,需要自己写代码来控制事务的提交、回滚,代码侵入强,重复代码多,不好维护,实际很少使用
- 声明式事务:通过AOP来实现,使用简单方便,可以统一处理事务,代码侵入低,一般使用此种方式,支持xml、注解2种配置方式
不管使用哪种方式,事务都是在service层控制的。
声明式事务
xml配置方式
<!-- 配置事务管理器,此处使用jdbc的事务管理器 -->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" /> <!-- 注入数据源 -->
</bean>
<!-- 配置实现事务的增强,此处只能用id,不能用name。transaction-manager指定要使用的事务管理器 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 指定要添加事务的方法,name指定方法名 -->
<!-- <tx:method name="transfer" /> -->
<!-- 可以使用通配符* -->
<!-- <tx:method name="*" /> -->
<tx:method name="save*" />
<tx:method name="find*" read-only="false"/>
<tx:method name="update*" />
<tx:method name="delete*" />
</tx:attributes>
</tx:advice>
<!-- 配置aop -->
<aop:config>
<!-- 配置事务的切入点 -->
<aop:pointcut id="txPointCut" expression="execution(* com.chy.mall.service.*.*(..))"/>
<!-- 建立事务切入点、事务增强之间的关联,会在指定切入点中找到指定的方法,给其施加增强(事务) -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
<tx:method name=“save*” />,name指定要添加事务的方法,是必需属性,此外还有4个可选属性
- propagation 指定事务的传播行为,默认为 REQUIRED
- isolation 指定事务的隔离级别,默认为 DEFAULT,即使用数据库自身默认的隔离级别,mysql默认为Repeatable read,oracle默认为Read committed。
- read-only 是否只读,读=>查询,写=>增删改。默认为false,可读可写。
- timeout 事务执行的超时时间,默认-1,永不超时
事务的隔离级别、传播行为一般不用设置,使用默认的即可
注解方式
传统的ssm、ssh项目需要在xml中配置事务管理器
<!-- 配置事务管理器,此处使用jdbc的事务管理器 -->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" /> <!-- 注入数据源 -->
</bean>
<!-- 启用事务管理的注解,需指定要使用的事务管理器 -->
<tx:annotation-driven transaction-manager="transactionManager" />
springboot项目则会自动配置事务管理器,无需手动配置
在service层要使用事务的地方加 @Transactional,可以加在方法上,也可以加在类|接口上(给所有方法添加事务)
//rollbackFor的值是class数组,指定抛出哪些异常时触发事务回滚
//此外同样可以指定事务的传播行为、隔离级别
@Transactional(rollbackFor = Exception.class)
使用注意点(使用了@Transactional但发生异常时没有回滚)
1、import导入@Transactional不是spring的
2、使用@Transactional时,在方法内部直接捕获处理完了异常。
在方法内部就处理完了异常,没有抛到外层的事务管理器的aop异常拦截点,自然触发不了回滚。
使用@Transactional时,在方法内部使用catch要慎重,就算需要使用catch记录流水、日志做一些处理,也应该考虑再次往外抛触发事务回滚。
spring的事务管理器捕获异常后,进行事务回滚,然后继续往外抛异常。
3、@Transactional没有设置rollbackFor = Exception.class。
缺省rollbackFor时,默认rollbackFor = RuntimeException.class 只在该方法抛出运行时异常时进行回滚。自定义的异常类通常是 extends Exception,不属于RuntimeException,如果方法内抛出的是自定义异常,默认不会触发事务回滚。
养成规范,@Transactional都加上rollbackFor = Exception.class。
4、service层的方法调用service的方法,主调方法没加@Transactional,被调方法加了@Transactional,则事务管理器只作用于被调方法。
被调方法出现异常时只回滚被调方法的操作、不回滚主调方法的操作;主调方法其它部分出现异常时,不会触发任何回滚。
5、数据表使用的存储引擎不支持事务,比如使用的是MyISAM。
使用过程中的常见问题
不能识别<tx:advice>、<tx:annotation-driven>
原因是IDEA自动导入的tx约束是旧版本的(已失效):
<!-- 命名空间部分 -->
xmlns:tx="http://www.springframework.org/schema/cache"
<!-- xsi部分 -->
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
换为新版本的即可:
<!-- 命名空间部分 -->
xmlns:tx="http://www.springframework.org/schema/tx"
<!-- xsi部分 -->
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
如果没有修改xsi中对应的部分,会报错
通配符的匹配很全面, 但无法找到元素 ‘tx:advice’ 的声明。
通配符的匹配很全面, 但无法找到元素 ‘tx:annotation-driven’ 的声明。
无法找到元素 tx:advice、tx:annotation-driven的声明
通配符的匹配很全面, 但无法找到元素 ‘tx:advice’ 的声明。
通配符的匹配很全面, 但无法找到元素 ‘tx:annotation-driven’ 的声明。
如果导入的tx约束没有问题,那一般是因为配置了tx的注解驱动<tx:annotation-driven> ,但从未使用@Transactional注解,即实际没有使用事务。
事务失效 | 不起作用
常见原因如下
- 数据库引擎不支持事务
- @Transactional 注解使用姿势不对:导入的注解不是spring的,隔离级别 propagation设置错误,异常回滚种类 rollbackFor 设置错误
- 方法权限修饰符为非public,@Transactional只拦截public型方法,只对public方法有效
- 方法中的异常已经被catch捕获处理了,没有抛出来,从而没有触发@Transactional的aop异常拦截回滚
spring提供的事务隔离级别
<xsd:attribute name="isolation" default="DEFAULT">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.transaction.annotation.Isolation"><![CDATA[
The transaction isolation level.
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="DEFAULT"/>
<xsd:enumeration value="READ_UNCOMMITTED"/>
<xsd:enumeration value="READ_COMMITTED"/>
<xsd:enumeration value="REPEATABLE_READ"/>
<xsd:enumeration value="SERIALIZABLE"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
spring定义了5种,其中4种是数据库自身提供的,DEFAULT是spring定义的,即使用数据库本身默认的隔离级别。
spring提供的事务传播行为
service层的方法调用service的方法,主调方法加了事务,被调方法可能加了事务也可能没加,事务传播行为定义了主调方法处理被调方法的事务的策略(主调需不需要把事务传播给被调)。
事务的传播行为讨论的前提是主调加了事务,不然就不存在事务的传播行为。
spring定义了7种传播行为
<xsd:attribute name="propagation" default="REQUIRED">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.transaction.annotation.Propagation"><![CDATA[
The transaction propagation behavior.
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="REQUIRED"/>
<xsd:enumeration value="SUPPORTS"/>
<xsd:enumeration value="MANDATORY"/>
<xsd:enumeration value="REQUIRES_NEW"/>
<xsd:enumeration value="NOT_SUPPORTED"/>
<xsd:enumeration value="NEVER"/>
<xsd:enumeration value="NESTED"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
这7种大致可分为3类
1、主调、被调使用同一个事务
-
REQUIRED :需要、必需,对于主调方法,事务是必需的。如果被调方法使用了事务,则主调方法使用被调方法的事务;如果被调方法没有使用事务,则创建一个新事务,管理整个主调方法(包括被调方法)。这是默认值,一般也是使用这种。
-
SUPPORTS:支持,被调有事务就支持,没有就算了。如果被调方法有事务,主调方法就使用被调方法的事务;如果被调方法没有事务,主调方法就不使用事务(都不使用事务)。
-
MANDATORY:强制,被调方法必须要有事务。如果被调使用了事务,则主调使用被调的事务;如果被调没有事务,则直接抛出异常。
2、主调、被调不使用同一个事务
-
REQUIRES_NEW:新建,被调的事务情况不变,给主调新建一个事务,管理主调方法中除被调方法以外的部分。
-
NOT_SUPPORTED:不支持,被调的事务情况不变,主调方法(除被调以外的部分)不使用事务。
-
NEVER:绝不,主调、被调都不使用事务,如果被调方法使用了事务,直接抛出异常。
3、嵌套事务
- NESTED:嵌套,在主调方法执行前,被调方法执行前、后设置还原点,执行失败时回滚到之前的还原点。