Spring事务管理之编程式事务场景及使用详解

问题引出:

当我在进行某一个业务开发时,需要先在主表存入一条数据逻辑上上是在主表存入一条数据以后从表再存入主表的附属信息。但是再出现运行过程 中出现了异常导致主表的数据存入进去了从表却没有信息。举个例子在进行转账的时候往往会从一个账户转出一定金额到另一个 账户,不允许一个人的
账户的余额已经减少了钱而另一个人的账户却没有收到转账的情况,那么再这种时候在业务层对 数据库进行操作的 时候就必须保证要么转成功(甲转出
了500元,乙的账户多了500元)要么全部失败(甲没有转出乙也没有收到),
这种保证了数据的完整性和一致性的特性正好符合事务的特性。下面就模拟一个转账的例子来看一看Spring中的事务带来的效果。
 首先描述一下业务:我在数据库中建立了一张账户表account里面有Id,balance两个字段。有一个操作account的Dao层 AccountDao具有转入
rollIn(int id,double money)和转出rollOut(int id,double money)两个方法。业务层AccountService封装 了一个转账的方法。
 AccountDao层代码:
public interface AccountDao {
	void rollIn(int id,double money);
	void rollOut(int id,double money);
}
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
	@Override
	public void rollIn(int id, double money) {
		String sql = "update account set balance = balance + ? where id = ?";
		this.getJdbcTemplate().update(sql,money,id);
	}
	@Override
	public void rollOut(int id, double money) {
		String sql = "update account set balance = balance - ? where id = ?";
		this.getJdbcTemplate().update(sql,money,id);
	}
}
 AccountService层代码:
public interface AccountService {
	void transfer(int in,int out,double money);
}
public class AccountServiceImpl implements AccountService {
	private AccountDao accountDao;
	public void setAccountDao(AccountDao accountDao) {
		this.accountDao = accountDao;
	}
	@Override
	public void transfer(int in, int out, double money) {
		accountDao.rollOut(out, money);
		accountDao.rollIn(in, money);
	}
}
 测试层代码:
public class SpringTest {
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		if(ac != null){
			AccountService accountService = (AccountService) ac.getBean("accountService");
			if(accountService != null){
				accountService.transfer(1, 2, 500d);
			}
		}
	}
}
 上述代码是在没有异常的情况下进行的,没有出现转丢的情况。现在人为的修改业务层的代码,模拟出现的异常。
public void transfer(int in, int out, double money) {
		accountDao.rollOut(out, money);
		String temp = null;
		temp.toCharArray();
		accountDao.rollIn(in, money);
	}
当代码执行到temp.toCharArray()的时候将会出现NullPointerExceprion异常,下面的账户转入操作将不会被执行,就出现了转丢钱的情况。
首先来看看Spring事务相关的三个关键类:
  • PlatformTransactionManager:事务管理器接口,对于实现者,Spring提供了众多根据持久层框架而适配的派生类,如 HibernateTransactionManagerDataSourceTransactionManager、JtaTransactionManager众多的派生类,具体参考官方API。
  • TransactionDefintion:事务定义信息,包括事务的隔离级别、传播行为、超时、只读等。
  • TransactionStatus:事务具体运行状态。
事务管理器接口PlatformTransactionManager
 事务定义信息TansactionDefintion
 事务隔离级别

传播行为

编程式事务:
  • 在AccountService中注入TransactionTemplate来简化事务管理的操作
  • 在TransactionTemplate中注入TransactionManager(真正的事务管理类)
  • 在TransactionManager中注入数据源(拿到连接对象)
 改造后的配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc" 
    xmlns:oxm="http://www.springframework.org/schema/oxm"
    xmlns:p="http://www.springframework.org/schema/p" 
    xmlns:tx="http://www.springframework.org/schema/tx" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    	http://www.springframework.org/schema/beans/spring-beans.xsd    
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd      
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd    
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd        
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">   
    <context:property-placeholder location="classpath:jdbc.properties"/>
	<!-- 配置数据源 -->
	<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
		<property name="driverClassName" value="${jdbc.driver}"/>
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
		<!-- 初始化连接大小 -->
		<property name="initialSize" value="0" />
		<!-- 连接池最大使用连接数量 -->
		<property name="maxActive" value="20" />
		<!-- 连接池最大空闲 -->
		<property name="maxIdle" value="20" />
		<!-- 连接池最小空闲 -->
		<property name="minIdle" value="0" />
		<!-- 获取连接最大等待时间 -->
		<property name="maxWait" value="60000" />
 </bean>
	<!-- 配置事务管理器 -->
	<bean id="transactionManager" class=" org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<!-- 参考JDBC进行事务管理的时候首先需要connection.setAutoCommit(false);具体的事务提交应该拿到连接对象,
			故在此注入数据源的信息 -->
		<property name="dataSource" ref="dataSource"/>		
	</bean>
	<!-- 配置事务管理的模板 注入模板是为了简化事务管理的底层操作 真正进行事务管理的是TransactionManager -->
	<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
		<!-- 在模板中注入事务管理器 -->
		<property name="transactionManager" ref="transactionManager"/>
	</bean>
	<!-- 配置业务 -->
	<bean id="accountService" class="AccountServiceImpl">
		<property name="accountDao" ref="accountDao"/>
		<property name="transactionTemplate" ref="transactionTemplate"/>
	</bean>	
	<!-- 配置数据库操作Dao层 -->
	<bean id="accountDao" class="AccountDaoImpl">
		<property name="dataSource" ref="dataSource"/>
	</bean>
</beans>  
 改造后的service代码
public class AccountServiceImpl implements AccountService {
	private AccountDao accountDao;
	
	public void setAccountDao(AccountDao accountDao) {
		this.accountDao = accountDao;
	}
	
	/*提供set方法供属性注入*/
	private TransactionTemplate transactionTemplate;
	public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
		this.transactionTemplate = transactionTemplate;
	}

	@Override
	public void transfer(final int in, final int out, final double money) {
		transactionTemplate.execute(new TransactionCallbackWithoutResult() {		
			@Override
			protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
				accountDao.rollOut(out, money);
				String temp = null;
				temp.toCharArray();
				accountDao.rollIn(in, money);
			}
		});
		
	}
}
 执行结果发现异常抛出,数据库里面的数据无仍和变化,既没有转出也没有转入。


 但是基于编程的事务不仅需要在使用事务管理的地方去进行注入,还需要自己对事物管理的代码进行编程,没有声明式事务来的那么方便。声明式事务请见下篇Spring事务管理之
声明式事务。








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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值