Spring 事务管理


 

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方法有效
  • service层的方法相互调用,一个没加事务的方法调用加了事务的方法,导致被调方法加的事务失效
  • 异常已经被catch捕获处理了,没有抛出来,从而没有触发@Transactional的aop异常拦截回滚
  • 使用的数据库引擎不支持事务

 

spring事务的实现原理 | @Transactional的实现机制

Spring的事务管理有3个核心接口

  • TransactionManager:事务管理器,用于管理事务
  • TransactionDefinition:事务定义,用于描述事务的定义,比如隔离级别、超时时间、传播行为等
  • TransactionStatus:事务状态,用于在事务管理过程中记录事务的状态
     

TransactionManager只是个标记接口(空接口),主要使用子接口 PlatformTransactionManager 平台事务管理器,PlatformTransactionManager 定义了2个方法:commit()、rollback()。主要的实现类是DataSourceTransactionManager,底层使用jdbc的事务管理。

 

spring的事务管理是通过aop实现的,给service层使用了事务的bean生成代理,增强使用事务的方法。

可在抽象类 TransactionAspectSupport 中查看事务切面的代码实现,主要看 invokeWithinTransaction() 方法

  • 在执行目标方法前配置事务属性
  • 把目标方法放在try中执行,目标方法执行过程中抛出 rollbackFor 指定的异常时,进行事务回滚,然后继续往外抛异常
  • 如果目标方法执行成功,则提交事务

实现类TransactionInterceptor,在 TransactionAspectSupport 的基础上,用于设置要使用的事务管理器、事务属性,调用目标方法。

 

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:嵌套,在主调方法执行前,被调方法执行前、后设置还原点,执行失败时回滚到之前的还原点。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值