简介
什么是事务?
事务是逻辑上的一组操作,要么都执行,要么都不执行.
事物的特性(ACID):
- 原子性: 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
- 一致性: 执行事务前后,数据保持一致;
- 隔离性: 并发访问数据库时,一个用户的事物不被其他事物所干扰,各并发事务之间数据库是独立的;
- 持久性: 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
Spring事务管理接口:
- PlatformTransactionManager: (平台)事务管理器
- TransactionDefinition: 事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)
- TransactionStatus: 事务运行状态
所谓事务管理,其实就是“按照给定的事务规则来执行提交或者回滚操作”。
PlatformTransactionManager接口介绍
Spring并不直接管理事务,而是提供了多种事务管理器 ,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。 Spring事务管理器的接口是: org.springframework.transaction.PlatformTransactionManager ,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。
PlatformTransactionManager接口代码如下:
PlatformTransactionManager接口中定义了三个方法:
Public interface PlatformTransactionManager()...{
// 根据指定的传播行为,返回当前活动的事务或创建一个新事务。
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
// 使用事务目前的状态提交事务
Void commit(TransactionStatus status) throws TransactionException;
// 对执行的事务进行回滚
Void rollback(TransactionStatus status) throws TransactionException;
}
我们在使用JDBC或者iBatis(就是Mybatis)进行数据持久化操作时,我们的xml配置通常如下:
<!-- 事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource" />
</bean>
TransactionDefinition接口介绍
事务管理器接口 PlatformTransactionManager 通过 getTransaction(TransactionDefinition definition) 方法来得到一个事务,这个方法里面的参数是 TransactionDefinition类 ,这个类就定义了一些基本的事务属性。
那么什么是事务属性呢?
事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。事务属性包含了5个方面。
TransactionDefinition接口中的方法如下:
TransactionDefinition接口中定义了5个方法以及一些表示事务属性的常量比如隔离级别、传播行为等等的常量。
我下面只是列出了TransactionDefinition接口中的方法而没有给出接口中定义的常量,该接口中的常量信息会在后面依次介绍到。
public interface TransactionDefinition {
// 返回事务的传播行为
int getPropagationBehavior();
// 返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据
int getIsolationLevel();
// 返回事务必须在多少秒内完成
//返回事务的名字
String getName();
int getTimeout();
// 返回是否优化为只读事务。
boolean isReadOnly();
}
TransactionStatus接口介绍
TransactionStatus接口用来记录事务的状态 该接口定义了一组方法,用来获取或判断事务的相应状态信息.
PlatformTransactionManager.getTransaction(…) 方法返回一个 TransactionStatus 对象。返回的TransactionStatus 对象可能代表一个新的或已经存在的事务(如果在当前调用堆栈有一个符合条件的事务)。
TransactionStatus接口内容如下:
public interface TransactionStatus{
boolean isNewTransaction(); // 是否是新的事务
boolean hasSavepoint(); // 是否有恢复点
void setRollbackOnly(); // 设置为只回滚
boolean isRollbackOnly(); // 是否为只回滚
boolean isCompleted; // 是否已完成
}
转账事务管理实例
准备工作:
创建一个t_account表并填充两条数据,表示两个账户余额情况
package cn.edu.zzti.cs.dao.transfer;
import org.apache.ibatis.annotations.Param;
//转账相关操作Mapper接口
public interface AccountMapper {
//更新转出账户中余额
Integer updateOutAccount(@Param("outAccount") String outAccount, @Param("money") Double money);
//更新转入账户中的余额
Integer updateInAccount(@Param("inAccount") String inAccount,@Param("money") Double money);
//根据账户查询账户余额
Double selectMoneyByAccount(@Param("account") String account);
}
Mapper映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="cn.edu.zzti.cs.dao.transfer.AccountMapper">
<update id="updateOutAccount">
UPDATE t_account
SET t_account.money = t_account.money - #{money}
WHERE t_account.account = #{outAccount}
</update>
<update id="updateInAccount">
UPDATE t_account
SET t_account.money = t_account.money + #{money}
WHERE t_account.account = #{inAccount}
</update>
<select id="selectMoneyByAccount" parameterType="java.lang.String" resultType="java.lang.Double">
SELECT money FROM t_account WHERE account = #{account}
</select>
</mapper>
创建转账业务接口以及实现类
package cn.edu.zzti.cs.service.transfer;
//转账业务接口
public interface AccountService {
void transfer(String outAccount,String inAccount,Double money);
}
package cn.edu.zzti.cs.service.transfer;
import cn.edu.zzti.cs.dao.transfer.AccountMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
//转账业务实现类
@Service
public class AccountServiceImpl implements AccountService{
@Autowired
private AccountMapper accountMapper;
@Override
public void transfer(String outAccount, String inAccount, Double money) {
if(accountMapper.selectMoneyByAccount(outAccount) >= money){
accountMapper.updateOutAccount(outAccount,money);
accountMapper.updateInAccount(inAccount,money);
}else {
System.out.println("账户余额不足!");
}
}
}
编写测试程序,测试转账结果
package cn.edu.zzti.cs.service.student;
import cn.edu.zzti.cs.service.transfer.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:/spring/applicationContext-service.xml")
public class AccountTest {
@Autowired
private AccountService accountService;
@Test
public void AccountServiceTest(){
accountService.transfer("a","b",100.00);
}
}
测试结果:转账成功
以上案例在转账过程中不出现异常时是可以实现转账业务的,一旦转账过程中出现错误,如:
if(accountMapper.selectMoneyByAccount(outAccount) >= money){
accountMapper.updateOutAccount(outAccount,money);
int i=1/0;//发生异常,使程序异常终止
accountMapper.updateInAccount(inAccount,money);
}else {
System.out.println("账户余额不足!");
}
有可能发生 a账户余额成功转出,中途程序异常终止,则b账户收不到转账。
再次进行测试,结果为:
这种情况就要添加事务管理,将转出与转入操作包含进一个事务中,实现要么都执行,要么都不执行的目的。
下面将转账业务实现类加以改造,为其添加事务。
1.编程式事务管理(不常用)
<!--事务标准配置-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!--(编程式事务管理配置)配置事务管理的模板:Spring为了简化事务管理的代码提供的类-->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
@Service
public class AccountServiceImpl implements AccountService{
@Autowired
private AccountMapper accountMapper;
/*编程式事务管理*/
@Autowired
TransactionTemplate transactionTemplate;
@Override
public void transfer(final String outAccount, final String inAccount, final Double money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
if(accountMapper.selectMoneyByAccount(outAccount) >= money){
accountMapper.updateOutAccount(outAccount,money);
accountMapper.updateInAccount(inAccount,money);
}else {
System.out.println("账户余额不足!");
}
}
});
}
}
2.声明式事务管理
声明式事务管理有三种方式,分别为基于TransactionProxyFactoryBean的方式、基于AspectJ的XML方式、基于注解的方式。后两种方式较为常用,在此只介绍后两种方式:
2.1 基于AspectJ的XML方式
这种方式不需要修改实现类代码,只需在spring配置文件中通过AOP配置切点和切面为目标方法添加事务。
package cn.edu.zzti.cs.service.transfer;
import cn.edu.zzti.cs.dao.transfer.AccountMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class AccountServiceImpl implements AccountService{
@Autowired
private AccountMapper accountMapper;
/*声明式事务管理*/
@Override
public void transfer(String outAccount, String inAccount, Double money) {
if(accountMapper.selectMoneyByAccount(outAccount) >= money){
accountMapper.updateOutAccount(outAccount,money);
//int i=1/0;
accountMapper.updateInAccount(inAccount,money);
}else {
System.out.println("账户余额不足!");
}
}
}
<!--事务标准配置-->
<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="**" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--通过AOP配置提供事务增强,让service包下所有Bean的所有方法拥有事务-->
<!--配置切面-->
<aop:config proxy-target-class="true">
<!--配置切入点-->
<aop:pointcut id="serviceMethod" expression="(execution(* cn.edu.zzti.cs.service.*..*(..)))"/>
<!--配置切面-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod"/>
</aop:config>
2.2 基于注解的方式
这种方式只需开启事务注解驱动并在实现类前添加注解,即能为其添加事务。
<!--事务标准配置-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 事务注解驱动,标注@Transactional的类和方法将具有事务性 -->
<tx:annotation-driven/>
package cn.edu.zzti.cs.service.transfer;
import cn.edu.zzti.cs.dao.transfer.AccountMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
/*@Transactional注解为此类所有方法添加事务*/
@Transactional
public class AccountServiceImpl implements AccountService{
@Autowired
private AccountMapper accountMapper;
/*声明式事务管理*/
@Override
public void transfer(String outAccount, String inAccount, Double money) {
if(accountMapper.selectMoneyByAccount(outAccount) >= money){
accountMapper.updateOutAccount(outAccount,money);
//int i=1/0;
accountMapper.updateInAccount(inAccount,money);
}else {
System.out.println("账户余额不足!");
}
}
}