@Transactional失效的场景

事务管理

事务管理是应用系统开发中必不可少的一部分。Spring 为事务管理提供了丰富的功能支持。
Spring 事务管理分为编码式和声明式的两种方式。
编程式事务指的是通过编码方式实现事务;
声明式事务基于 AOP,将具体业务逻辑与事务处理解耦。
声明式事务管理使业务代码逻辑不受污染, 因此在实际使用中声明式事务用的比较多。

声明式事务有两种方式

一种是在配置文件(xml)中做相关的事务规则声明,另一种是基于@Transactional注解的方式。注释配置是目前流行的使用方式,因此本文将着重介绍基于@Transactional注解的事务管理。

1.在 xml 配置文件中添加事务配置信息。

<!-- TransactionManager定义。 -->
	<bean id="transactionManager" class="com.xxx.cms.xxx.common.frame.spring.transaction.BaseTransactionManager">
		<property name="dataSource" ref="dataSource"/>
	</bean>
	<aop:aspectj-autoproxy proxy-target-class="true" />

	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="add*" propagation="REQUIRED" read-only="false" />
			<tx:method name="del*" propagation="REQUIRED" read-only="false"/>
			<tx:method name="move*" propagation="REQUIRED" read-only="false"/>
			<tx:method name="modify*" propagation="REQUIRED" read-only="false"/>
			<tx:method name="update*" propagation="REQUIRED" read-only="false"/>
			<tx:method name="batch*" propagation="REQUIRED" read-only="false"/>
			<tx:method name="sync*" propagation="REQUIRED" read-only="false" />
			<tx:method name="save*" propagation="REQUIRED" read-only="false"/>
            <tx:method name="accept*" propagation="REQUIRED" read-only="false"/>
            <tx:method name="capturePicBy*" propagation="NESTED" read-only="false"/>
			<tx:method name="fetch*" propagation="SUPPORTS" read-only="false" />
            <tx:method name="get*" propagation="SUPPORTS" read-only="false" />
			<tx:method name="find*" propagation="SUPPORTS" read-only="false" />
			<tx:method name="check*" propagation="SUPPORTS" read-only="false" />
			<tx:method name="search*" propagation="SUPPORTS" read-only="false" />
			<tx:method name="*" propagation="SUPPORTS" read-only="false" />
		</tx:attributes>
	</tx:advice>
	<!--把事务控制在Service层 &amp; 代表转义后的&-->
    <!--1.先看bean类或方法上是否有{@link org.springframework.transaction.annotation.Transactional}注解,如果有,按照注解的方式生成代理-->
    <!--2.再看bean类或方法上是否有{@link NotTransactionProxy}注解,如果有,那么将不会生成事务代理-->
    <!--3.最后和{@code data-source-beans.xml}中的配置相比较,按照配置文件中的方法名,生成指定的代理-->
	<aop:config>
		<aop:pointcut id="pc"
			expression="execution( * com.xxx.cms.xxx..*.service..*.*(..)) and
			!@annotation(org.springframework.transaction.annotation.Transactional) and
			!@annotation(com.xxx.cms.xxx.common.frame.spring.transaction.NotTransactionProxy) and
			!@within(com.xxx.cms.xxx.common.frame.spring.transaction.NotTransactionProxy) and
			!@within(org.springframework.transaction.annotation.Transactional)" />
		<aop:advisor pointcut-ref="pc" advice-ref="txAdvice" />
	</aop:config>

使用xml配置的方式,需要显式的配置aop,这点比较麻烦
但是可以从全局统一进行事务的管理,比较方便

2.采用@Transactional注解进行声明式事务的管理

相比纯xml配置,使用@Transactional 注解管理事务要简单的多。
@Transactional管理事务的实现步骤分为两步。

<bean id="transactionManager" class="com.xxx.cms.xxx.common.frame.spring.transaction.BaseTransactionManager">
		<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven/>

1.首先配置事务管理器
2.使用<tx:annotation-driven/>开启对@Transactional的支持

除了用配置文件的方式,@EnableTransactionManagement注解也可以启用事务管理功能。

@Transactional的属性

属性名说明
name当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器
propagation事务的传播行为,默认值为 REQUIRED。
isolation事务的隔离度,默认值采用 DEFAULT
timeout事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。
read-only指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。
rollback-for用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。
no-rollback- for抛出 no-rollback-for 指定的异常类型,不回滚事务。

@Transactional

1.类级别:表示该类中的所有公共方法配置相同的事务信息
2.方法级别:表示调用该方法时会进行相应的事务管理
当类和方法同时存在@Transactional时,方法级的会覆盖类级别的@Transactional

@Transactional的工作原理

1.调用声明@Transactional的目标方法时,Spring Framework 默认使用 AOP代理,在代码运行时生成一个代理对象
2.根据@Transactional的属性配置信息,这个代理对象决定该声明@Transactional的目标方法是否由拦截器 TransactionInterceptor 来使用拦截。
3.在 TransactionInterceptor拦截时,会在在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑, 最后根据执行情况是否出现异常,利用抽象事务管理器(AbstractPlatformTransactionManager )操作数据源 DataSource 提交或回滚事务,

@Tansactional失效的情况

前面介绍了声明式事务管理的两种方式,基于注解的事务管理已经成为了主流,但是有时候使用了@Transactional也不一定会生效
在以下几种情况下,@Transactional会失效

1.在接口上使用@Transactional

spring使用的代理有两种 CglibAopProxyJdkDynamicAopProxy 两种。

如果使用了Cglib代理,则spring无法为接口生成代理对象,就无法在目标方法执行之前创建并加入事务。

2.@Transactional 应用在非 public修饰的方法上

之所以会失效是因为在Spring AOP 代理时,TransactionInterceptor(事务拦截器)在目标方法执行前后进行拦截,Spring代理工厂在启动时,会扫描所有类和方法,并会检查目标方法的修饰符是否为 public,不是 public则不会获取@Transactional的属性配置信息。

3.@Transactional注解属性 propagation设置错误

这种失效是由于配置错误,若是错误的配置以下三种 propagation,事务将不会发生回滚。

TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。

TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

这3种都是因为以非事务的方式运行,所以事务管理失效。

4. @Transactional 注解属性rollbackFor设置错误

Spring默认抛出了未检查unchecked异常(继承自 RuntimeException 的异常)或者 Error才回滚事务;其他异常不会触发回滚事务,若想spring为抛出的其他异常进行回滚,需要在rollback中进行指定。

5.同一个类中方法调用,导致@Transactional失效

比如有一个类Test,它的一个方法A,A再调用本类的方法B(不论方法B是用public还是private修饰),但方法A没有声明注解事务,而B方法有。则外部调用方法A之后,方法B的事务是不会起作用的。这也是经常犯错误的一个地方。

那为啥会出现这种情况?其实这还是由于使用Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。

6. 异常被你try…catch…了,导致@Transactional失效

如果你在目标方法中try…catch…了异常,那么spring是感知不到的,也就不会进行回滚

7.数据库引擎不支持事务

事务能否生效数据库引擎是否支持事务是关键。常用的MySQL数据库默认使用支持事务的innodb引擎。一旦数据库引擎切换成不支持事务的myisam,那事务就从根本上失效了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值