事务管理的两个方式 |Java 开发实战

这是我参与更文挑战的第3天,活动详情查看: 更文挑战

本文正在参加「Java主题月 – Java 开发实战」,详情查看 活动链接

前言

什么是事务?

事务指的是逻辑上的一组操作,这组操作要么全部成功,要么全部失败。
####事务的特性:

  • 原子性
    原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
  • 一致性
    一致性指事务前后数据的完整性必须保持一致。
  • 隔离性
    隔离性指多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之前要相互隔离。
  • 持久性
    持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,即使数据库发生故障也不应该对其有任何影响。

spring事务管理的方式可以分为两种:编程式事务管理声明式事务管理

编程式事务管理

基于底层API事务管理的例子:

public class BankServiceImpl implements BankService {
private BankDao bankDao;
private TransactionDefinition txDefinition;
private PlatformTransactionManager txManager;
 
public boolean transfer(Long fromId, Long toId, double amount) {
  TransactionStatus txStatus = txManager.getTransaction(txDefinition);
  boolean result = false;
  try {
    result = bankDao.transfer(fromId, toId, amount);
    txManager.commit(txStatus);
  } catch (Exception e) {
    result = false;
    txManager.rollback(txStatus);
    System.out.println("Transfer Error!");
  }
  return result;
  }
}
复制代码

基于底层API的事务管理示例配置文件:

<?xml version="1.0" encoding="utf-8"?>
<bean id="bankService" class="footmark.spring.core.tx.programmatic.origin.BankServiceImpl">
<property name="bankDao" ref="bankDao"/>
<property name="txManager" ref="transactionManager"/>
<property name="txDefinition">
<bean class="org.springframework.transaction.support.DefaultTransactionDefinition">
<property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/>
</bean>
</property>
</bean>
复制代码

如上所示,在类中增加了两个属性:一个是 TransactionDefinition 类型的属性,它用于定义一个事务;另一个是 PlatformTransactionManager 类型的属性,用于执行事务管理操作。

如果方法需要实施事务管理,我们首先需要在方法开始执行前启动一个事务,调用PlatformTransactionManager.getTransaction(…) 方法便可启动一个事务。创建并启动了事务之后,便可以开始编写业务逻辑代码,然后在适当的地方执行事务的提交或者回滚。

基于 TransactionTemplate 的编程式事务管理

通过前面的示例可以发现,这种事务管理方式很容易理解,但令人头疼的是,事务管理的代码散落在业务逻辑代码中,破坏了原有代码的条理性,并且每一个业务方法都包含了类似的启动事务、提交/回滚事务的样板代码。幸好,Spring 也意识到了这些,并提供了简化的方法,这就是 Spring 在数据访问层非常常见的模板回调模式。

txTemplate.execute(new TransactionCallbackWithoutResult() {
	@Override
	protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
		if (userNoticeBo.getUserContract() != null) {
			userContractMapper.insertAndUpdate(userNoticeBo.getUserContract());
			IousWhiteUser iousWhiteUser = iousWhiteUserMapper.selectByUserId(userNoticeBo.getUserContract().getUserId());
			if ((iousWhiteUser == null || !UserInWhiteListEnum.IN_WHITE_LIST.getCode().equals(iousWhiteUser.getWhiteStatus()))
			&& UserInWhiteListEnum.IN_WHITE_LIST.equals(userNoticeBo.getUserContract().getIsInWhiteList())){
				iousWhiteUserMapper.insert(createInsertBody(userNoticeBo.getUserContract()));
			}
		}
		userInfoRecordMapper.insertSelective(userNoticeBo.getUserInfoRecord());
		if (userNoticeBo.getBusiNotice() != null) {
			busiNoticeMapper.insertSelective(userNoticeBo.getBusiNotice());
		}
		if(userNoticeBo.getExtNoticeInfo() != null){
			extNoticeInfoMapper.insertSelective(userNoticeBo.getExtNoticeInfo());
		}
		if(userNoticeBo.getTblCampaignAudience() != null){
			audienceService.saveOrUpdateAudience(userNoticeBo.getTblCampaignAudience());
		}
	}
}
);
复制代码

相应的xml配置:

<tx:annotation-driven transaction-manager="ctripcfbTxManager"/>
<bean id="ctripcfbTxManager" 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="ctripcfbTxManager" />
</bean>
复制代码

TransactionTemplate 的 execute() 方法有一个 TransactionCallback 类型的参数,该接口中定义了一个 doInTransaction() 方法,通常我们以匿名内部类的方式实现 TransactionCallback 接口,并在其 doInTransaction() 方法中书写业务逻辑代码。这里可以使用默认的事务提交和回滚规则,这样在业务代码中就不需要显式调用任何事务管理的 API。doInTransaction() 方法有一个TransactionStatus 类型的参数,我们可以在方法的任何位置调用该参数的 setRollbackOnly() 方法将事务标识为回滚的,以执行事务回滚。

根据默认规则,如果在执行回调方法的过程中抛出了未检查异常,或者显式调用了TransacationStatus.setRollbackOnly() 方法,则回滚事务;如果事务执行完成或者抛出了 checked 类型的异常,则提交事务。

TransactionCallback 接口有一个子接口 TransactionCallbackWithoutResult,该接口中定义了一个 doInTransactionWithoutResult() 方法,TransactionCallbackWithoutResult 接口主要用于事务过程中不需要返回值的情况。当然,对于不需要返回值的情况,我们仍然可以使用 TransactionCallback 接口,并在方法中返回任意值即可。

声明式事务管理

基于 @Transactional 的声明式事务管理

启用后处理Bean的配置

<tx:annotation-driven transaction-manager="transactionManager"/>
复制代码

与前面相似,transaction-manager 属性的默认值是 transactionManager,如果事务管理器 Bean 的名字即为该值,则可以省略该属性。

虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 小组建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。

总结

声明式事务管理的使得纯业务代码不被污染,后期代码的维护更加的方便,这也是spring官方推荐的事务管理的方式。和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享