这是我参与更文挑战的第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官方推荐的事务管理的方式。和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。