导览
前言
书接上文(快捷入口),马不停蹄,本文开始Spring事务的正式介绍。
Q:Spring事务是如何管理的
全面的事务支持是使用Spring框架的最引人注目的原因之一。Spring 框架为事务管理提供了一致的抽象,提供了以下好处:
- 跨不同事务API
如Java事务API (JTA)、JDBC、Hibernate和Java持久性API (JPA)的一致编程模型。 - 支持声明式事务管理
如@Transactional
,可以用在类(整个类受事务管理)或方法(单方法事务管理)上。
1. Spring事务传播行为
- @Transactional(propagation=Propagation.
REQUIRED
)
如果有事务,那么加入事务,没有会新建一个(默认) - @Transactiona(propagation=Propagation.
NOT_SUPPORTED
)
容器不为这个方法开启事务 - @Transactional(propagation=Propagation.
REQUIRES_NEW
)
不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务 - @Transactional(propagation=Propagation.
MANDATORY
)
必须在一个已有的事务中执行,否则抛出异常 - @Transactional(propagation=Propagation.
NEVER
)
必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反) - @Transactional(propagation=Propagatior.
SUPPORTS
)
如果其他bean调用这个方法,在其他bean中声明事务,那就用事务。如果其他bean没有声明事务,那就不用事务
下面通过一张图来呈现七类传播行为的传递情况:
2. @Transactional原理
2.1 自动提交
默认情况下,数据库处于自动提交模式。(mysql自动提交,oracle 默认不提交)
每一条语句处于一个单独的事务中,在这条语句执行完毕时,如果执行成功则隐式的提交事务,如果执行失败则隐式的回滚事务。
事务管理,是一组相关的操作处于一个事务之中,因此必须关闭数据库的自动提交模式。
这点,Spring 会在 org/springframework/jdbc/datasource/DataSourceTransactionManager.java 中,将底层连接的自动提交属性设置为 false
。
2.2 Spring 事务回滚规则
Spring事务管理器回滚一个事务的推荐方法是在当前事务的上下文内抛出异常。Spring事务管理器会捕捉任何未处理的异常,然后依据规则决定是否回滚抛出异常的事务。默认配置下,Spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚)。而抛出checked异常则不会导致事务回滚。
2.3 注意事项
由于Spring事务管理是基于接口代理或动态字节码技术,通过AOP实施事务增强的。那么需要注意以下2个问题:
- 对于基于接口动态代理的AOP事务增强来说,由于接口的方法是public的,这就要求实现类的实现方法必须是public的(不能是protected,private等),同时不能使用static的修饰符。所以,可以实施接口动态代理的方法只能是使用
public
或public final
修饰符的方法,其它方法不可能被动态代理,相应的也就不能实施AOP增强,也即不能进行Spring事务增强。 - 基于CGLib字节码动态代理的方案是通过扩展被增强类,动态创建子类的方式进行AOP增强植入的。由于使用final、static、private修饰符的方法都不能被子类覆盖,相应的,这些方法将不能被实施的AOP增强。
综上,必须特别注意这些修饰符的使用,@Transactional
注解只被应用到 public
可见度的方法上。
如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它不会报错,但是这个被注解的方法将不会被事务处理。
3. Spring事务自我调用
从图中可以看出,调用事务时首先调用的是AOP代理对象而不是目标对象, 首先执行事务切面,事务切面内部通过 TransactionInterceptor
环绕增强实现事务的增强。即进入目标方法之前开启事务,退出目标方法时提交/回滚事务。
4. Spring事务配置
最后我们看看基于Spring的事务配置是如何完成的。
step1:导入命名空间和规范
<!-- applicationContext.xml -->
<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-3.0.xsd
http://wwW.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://uW.springframelwork.org/schema/context
http://wMw.springframework.org/schema/context/spring-context-3.0.xsd">
重点配置如下所示:
xmlns:tx=“http://wwW.springframework.org/schema/tx”
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
step2:配置事务管理器
<!--配置声明式事务管理器-->
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!--注解驱动-->
<tx:annotation-driven transaction-manager="txManager"/>
step3:声明事务
<!--使用声明方式配置事务(含传播行为)-->
<tx:advice id="TestAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="query*" read-only="true"/>
<tx:method name="insert*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
<tx:method name="delete*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
</tx:attributes>
</tx:advice>
step4:配置参与事务的类
<aop:config>
<aop:pointcut id="allTestServiceMethod" expression="execution(* com.test.service.*.*(..))"/>
<aop:advisor pointcut-ref="allServiceMethod" advice-ref="TestAdvice" />
</aop:config>
其核心是:expression="execution(* com.test.service.*.*(..))"
。
这里博主特别强调一下:
– 第1个 星号 代表返回值
– 第2个 星号 代表service下的子包
– 第3个 星号 代表具体的方法名
– 最后的(…)代表方法参数
step5:定义一个类
必须使用@Transactional
注解。
@Transactional
public class FirstServiceImpl implements FirstService{
// TODO
}
结语
通过本文与上文,博主通过总结事务的基础原理和Spring的事务管理框架,特别呈现了我们在实际应用中,应该如何合理、正确的使用事务。这通常也是面试过程中会遇到的经典问题,相信通过上、下两篇精讲文章,能够为需要的盆友提供一些帮助。
走过的、路过的盆友们,点点赞,收收藏,并加以指导,以备不时之需哈~
精彩回顾
一文读懂事务及Spring事务的管理(面试经)_上篇
一文读懂Spring Security的工作原理和机制(面试经)
一文读懂Spring AOP的工作原理和机制(面试经)
一文读懂Spring IoC的工作原理和机制(面试经)
一文读懂SpringMVC的工作原理
Springboot中基于X509完成SSL检验的原理与实践
基于springboot+enum配置化实践