Spring事务管理
事务
- 含义:
- 访问并可能更新数据库中各种数据项的一个程序执行单元(unit),是一个序列的对数据库的读/写的操作。
- 目的:
- 保证用户的每一次操作都是可靠的,即便出现了异常的访问情况,也不至于破坏后台数据的完整性。
- 为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。
- 当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。
- 特性:
- 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
- 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。
- 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
- 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中。
Spring事务管理
- 方式
- 编程式事务管理(精确控制事务边界)
- 声明式事务管理(低耦合)
事务管理器
DataSourceTransactionManager:用于Spring对JDBC抽象的支持,也可用于iBATIS进行持久化的场景。
<!-- JDBC事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 属性为引用了一个id为dataSource的bean--> <property name="dataSource" ref="dataSource"/> </bean>
DataSourceTransactionManager通过调用
java.sql.Connection
来管理事务,后者是通过DataSource获取的,通过调用连接的commit()
方法来提交事务。事务失败,则调用其rollback()
方法进行回滚。- HibernateTransactionManager:用于对Hibernate进行持久化
sessionFactory需要装配一个Hibernate的SessionFactory。 - JpaTransactionManager:用于对Java持久化API进行持久化
- JtaTransactionManager:用于分布式事务,如:跨多个事务资源(一个以上数据库)
一个转账示例(伪代码)
业务层的转账方法代码
public void transferMoney(String from, String to, double money) {
//调用持久层Dao类的相关方法
//outMoney():转出方法 inMoney():转入方法
accountDao.outMoney(from, money);
accountDao.inMoney(to, money);
}
@Test
public void testTransfer() {
//调用业务层的转账方法
accountService.transferMoney("lily", "lucy", 200d);
}
上边这个转账方法就必须进行事务管理,要么成功,要么失败。但是,如果
public void transferMoney(String from, String to, double money) {
//调用持久层Dao类的相关方法
//outMoney():转出方法 inMoney():转入方法
accountDao.outMoney(from, money);
int i = 10 / 0;//这里出现异常
accountDao.inMoney(to, money);
}
转账期间出现异常时,转账失败,造成钱转出,对方却没收到的情况,so,交给事务吧!
+ 采用编程式事务管理
+ 首先,我们需要使用Spring的TransactionTemplate来添加事务边界,TransactionTemplate提供了一种回调机制。在配置文件中注入TransactionTemplate
<!-- 配置c3p0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 加载jdbc的属性文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 配置Dao层类 -->
<bean id="accountDao" class="com.mvbin.dao.AccountDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置业务层类 -->
<bean id="accountService" class="com.mvbin.service.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="transactionTemplate" ref="transactionTemplate"/>
</bean>
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<!-- 需要注入transactionManager -->
<property name="transactionManager" ref="transactionManager"/>
</bean>
<!-- JDBC事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 属性为引用了一个id为dataSource的bean-->
<property name="dataSource" ref="dataSource"/>
</bean>
+ 其次,修改`transferMoney()`
public void transferMoney(final String from, final String to, final double money) {
/*set注入transactionTemplate
* 调用execut(),传入TransactionCallback<T>对象,匿名内部类
*
*/
transactionTemplate.execute(new TransactionCallback<String>() {
public String doInTransaction(TransactionStatus txStatus) {
try {
accountDao.outMoney(from, money);
int i = 10 / 0;//异常
accountDao.inMoney(to, money);
} catch (RuntimeException e) {
txStatus.setRollbackOnly();
throw e;
}
return "";
}
});
}
这种就是编程式事务管理了,对程序有较大的侵入性
+ 采用声明式事务管理:通过事务属性进行定义,通过AOP框架进行实现。
+ 事务属性
+ 传播行为
+ 隔离级别
+ 回滚规则
+ 事务超时
+ 是否只读
+ 使用tx命名空间配置事务
<!-- 配置事务:配置所需管理器,配置事务属性 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 事务属性配置 -->
<!--
name: 方法名称,可用通配符进行匹配一系列方法
isolation: 事务隔离级别
propagation: 事务传播行为
read-only: 事务是否只读
timeout: 定义事务超时时间
no-rollback-for:对于指定异常出现时不进行回滚
rollback-for: 对于指定异常出现时进行回滚
-->
<tx:attributes>
<!-- 以 transfer开头的方法需要在事务中运行-->
<tx:method name="transfer*" propagation="REQUIRED"/>
<!-- 其他方法,如果存在当前事务,就在事务中运行,否则不运行 -->
<tx:method name="*" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
<!-- 定义一个通知器 -->
<aop:config>
<!--
advice-ref:应用一个通知
pointcut:定义一个切面,以AspectJ切入点表达式来声明通知器适用于com.mvbin.service.AccountService接口中的所有方法
-->
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.mvbin.service.AccountService.*(..))"/>
</aop:config>
+ 通过注解驱动配置事务
<!--
配置这样一句话即可,注解驱动会检查Spring上下文中所有的Bean,并找出使用@Transactional注解
的Bean,不管这个注解是在类上还是方法上,<tx:annotation-driven>会自动为其添加事务通知。
通知事务的属性通过@Transactional注解的参数来定义的。
-->
<tx:annotation-driven transaction-manager="transactionManager"/>
业务类代码
@Transactional(propagation=Propagation.SUPPORTS, readOnly=true)
public class AccountServiceImpl implements AccountService {
private AccountDaoImpl accountDao;
private TransactionTemplate transactionTemplate;
@Transactional(propagation=Propagation.REQUIRED, readOnly=false)
public void transferMoney(final String from, final String to, final double money) {
//此处省略一万行代码。。。。
}