Spring-Spring事务管理
Spring是一般系统架构中的管理者,负责管理其它框架,协调各个部分的工作。Spring的事务管理有两种编程式事务和声明式事务,在了解如何使用Spring事务之前我们先了解一下与事务/Spring事务有关的一些概念。
什么是事务
概要
事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。在事务的一个执行单元中的一系列操作,要么完全执行,要么完全不执行。
特性
原子性
事务是一个不可分割的工作单位,事务中的操作要么完全执行,要么完全不执行
一致性
事务前后数据的完整性必须保持一致,组成一个事务的各种操作,要么全部执行成功,要么全部执行失败。
隔离性
指多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间需要相互隔离
持久性
指一个事务一旦被提交,它对数据库中的数据的改变就是永久性的,即使数据库发生故障也不应该对其有任何影响
Spring事务管理
Spring管理高层抽象主要包括3个接口,分别是
·PlatformTransactionManager事务管理器
·TransactionDefinition(事务定义信息(隔离、传播、超时、只读)
·TransactionStatus事务具体运行装填
下面就各自简单的介绍一下这几个接口。
PlatfromTransactionManager
Spring为不同的持久化框架提供了不同的PlatformTransactionManager接口实现,如下图
事务 | 说明 |
org.springframework.jdbc.datasource.DataSourceTransactionManager | 使用Spring JDBC或ibatis/Mybatis进行持久化时使用 |
org.springframework.orm.hibernate3(4).HibernateTransacionManager | 使用Hibernate3.0(4.0)版本进行持久化数据时使用 |
org.springframework.orm.jpa.JpaTransactionManager | 使用JPA进行持久化时使用 |
org.springframework.orm.jdo.JdoTransactionManager | 当持久化机制是Jdo时使用 |
······ | ······ |
TransactionDefinition
如果在事务中我们不考虑隔离性,可能会应发:脏读、不可重复读、幻读
脏读
一个事务中读取了另外一个事务改写但还未提交的数据,如果这些数据被回滚,则读到的数据是无效的。
不可重复读
在同一事务中,多次读取同一个数据返回的结果有所不同。
幻读
一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了,再后来的查询中,第一个事务就会发现有些原来没有得记录。
事务的几种隔离级别
隔离级别 | 含义 |
DEFAULT | 使用后端数据库默认的隔离级别(spring中的选择项) |
READ_UNCOMMITED | 允许读取还未提交的改变了的数据,可能导致脏读、幻读、不可重复读 |
READ_COMMITED | 允许在并发事务已经提交后读取,可防止脏读,但幻读和不可重复读仍可发生 |
REPEATABLE_READ | 对相同字段的国瓷读取是一直的,除非数据被事务本身改变。可防止脏读和不可重复读,但幻读仍可能发生 |
SERIALIZABLE | 完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。 |
事务的传播行为
当有复杂的方法需要service中的另外两个方法才能完成是,这个时候我们就要考虑使用事务的传播行为解决service中方法相互调用的问题。
事务传播行为类型 | 说明 |
PROPAGATION_REQUIRED | 支持当前事务,如果不存在就新建一个事务 |
PROPAGATION_SUPPORTS | 支持当前事务,如果不存在,就不使用事务 |
PROPAGATION_MANDATORY | 支持当前事务,如果不存在,就抛出异常 |
PROPAGATION_REQUIRES_NEW | 如果有当前事务存在,挂起当前事务,创建一个新的事物 |
PROPAGATION_NOT_SUPPORTED | 以非事务的方式运行,如果有当前事务存在,挂起当前事务 |
PROPAGATION_NEVER | 以非事务的方式运行,如果当前事务存在,就抛出异常 |
PROPAGATION_NESTED | 若果当前事务存在,则嵌套事务执行 |
事务的传播行为主要解决业务层方法间的相互调用所产生的事务应该如何进行传递的问题。
超时
设置事务的超时时间
只读
该事务只允许读,不允许增、删、改。
TransactionStatus
其实是SavepointManager接口的实现,接口,执行一个API以编程的方式管理事务以通用的方式保存事务节点状态。
Spring管理事务的两种方式
编程式事务
该方式在实际开发过程中基本不用,通过TransactionTemplate手动管理事务,与业务层代码耦合,增加了开发人员的工作量,同时不利于后期的维护拓展。
编程式事务分为这么几步:配置事务管理器、配置模板、业务层注入、业务层代码编写。
配置文件
<!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 配置模板 --> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"/> </bean> <!-- 业务注入 --> <bean id="testService" class="com.cody.service.TestService"> <property name="testDao" ref="testDao"/> <property name="transactionTemplate" ref="transactionTemplate"/> </bean>
public class TestServiceImpl implements TestService { //注入事务管理模板 private TransactionTemplate transactionTemplate; public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; } @Override public void Transferable(){ transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus arg0) { //具体的业务实现 ······ } }) } }
声明式事务一共有三种,下面我们一一道来。
使用XML声明式
<!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 配置业务层代理 --> <bean id="testServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!-- 配置目标对象 --> <proterty name="target" ref="testService"/> <!-- 注入事务管理器 --> <proterty name="transactionManager" ref="transactionManager"/> <!-- 注入事务属性 --> <property name="transactionAttributes"> <props> <!-- *PROPAGATION :事务的传播行为 *ISOLATION :事务的隔离级别 *readOnle :只读 *-Exception :发生哪些异常回滚 *+Exception :发生哪些异常不回滚 --> <prop key="transfer">PROPAGATION_REQUIRED</prop> </props> </property> </bean>
基于tx/aop的XML方式
<!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 配置事务通知:(事务增强) --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- 传播行为 --> <tx:method name="transfer" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!-- aop配置切面 --> <aop:config> <!-- 配置切入点 --> <aop:pointcut expression="execution(* com.cody.service.impl.*.*(..))" id="pointcut"/> <!-- 配置切面 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/> <!-- 简写模式 --> <!-- <aop:advisor advice-ref="txAdvice" pointcut="execution(* cn.itcast.ssm.service.impl.*.*(..))"/> --> </aop:config>
基于注解的方法
这种方法配置文件如下,由于需要在业务层中添加注解不利于维护和以后的修改。
<!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <tx:annotation-driven/>
总结
在笔者的项目中使用的是AspectJ的XML方法,当然这也是最常用的方法,希望笔者的这篇文章对正在了解这方面知识的朋友们提供到帮助。