最近初学Spring框架,今天写一篇博客来记录我学习的事务管理器,作为第一篇博客。
了解什么是事务?个人理解:在一个事务(工作单元)中存在多个动作,只有当所有动作都成功时,这个事务才可以成功执行;否则,只要有一个动作失败,那么整个事务就算做失败。
eg:在书店使用会员卡买一本书,书店从库存中找到存书是一个动作,会员卡扣款是一个动作,库存-1是一个动作,以上三个动作只要有一个无法完成或者无法得到预期结果,整个事务就无法进行,只有以上三个动作都成功,买书的事务才可以成功。
事务符合ACID(以下四条摘自百度百科):
原子性(Atomicity):整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。
一致性(Consistency):一个事务可以封装状态改变(除非它是一个只读的)。事务必须始终保持系统处于一致的状态,不管在任何给定的时间并发事务有多少。
隔离性(Isolation):隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作。
持久性(Durability):在事务完成以后,该事务对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。
为什么要进行事务管理?进行事务管理,保证数据的完整性和一致性。
eg:仍以之前的买一本书为例,如果查书,扣款,出库三个动作是分开的,那么如果查书,扣款都成功了,而出库时由于库存不足导致失败,而前两项动作已经完成了修改数据库,就会出现扣款了却没有拿到书的情况,这时我们数据库中的数据将无法保持一致性和完整性,因此需要将以上三个动作通过事务管理器定义为同一件事务,如此就可以防止出现此种状况。
Spring提供了三种不同的事务管理器:DataSourceTransactionManager、JtaTransactionManager、HibernateTransactionManager。眼下我只学习了DataSourceTransactionManager,其他以后再说。
首先,在Spring框架下配置事务管理器。在.xml文件创建时勾选tx,在代码<beans></beans>对标签之间加入以下代码,启动事务管理器
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 配置数据源 -->
<property name="dataSource" ref="druidDataSource"/>
</bean>
<!-- 启动事务注解配置 -->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
而后,在类中就可以使用@Transactional注解了,该注解可以使用在方法上,也可以使用在类名上,表示该类下的方法都是事务方法。
@Transactional拥有propagation(REQUIRED),isolation,noRollbackFor,noRollbackForClassName,readOnly(false),
rollbackFor,rollbackForClassName,timeout,transactionManager,value共10个属性,括号内为默认值。
noRollbackFor,noRollbackForClassName两个属性用于对特定异常类(特定一组异常类名),遇到时不执行回滚;
rollbackFor,rollbackForClassName两个属性用于对特定异常类(特定一组异常类名),遇到时执行回滚;
readOnly属性用于定义该事务是否为只读事务,如果定义为只读事务,将无法完成对数据库的变更操作(增删改),只能执行查询;
isolation事务的隔离级别(不懂);
timeout超时时间,以s为单位;
value可选的限定描述符,指定使用的事务管理器;
transactionManager用于多线程情况(不懂);
propagation事务的传播行为(重点,常用):
这个属性的取值是枚举org.springframework.transaction.annotation.Propagation的值
总共有{"MANDATORY","NESTED","NEVER","NOT_SUPPORTED","REQUIRED","REQUIRES_NEW","SUPPORTS"}
它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播
下面主要介绍这些取值代表的含义:
"MANDATORY":使用当前的事务,如果当前没有事务,就抛出异常。特点是要求当前调用者必须是事务。
"NESTED":如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则新建一个事务。
"NEVER":以非事务方式执行,如果当前存在事务,则抛出异常。特点是要求当调用者不能是事务。
"NOT_SUPPORTED":以非事务方式执行操作,如果当前存在事务,就把当前事务挂起(暂停)。
"REQUIRED":如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。
"REQUIRES_NEW":新建事务,如果当前存在事务,把当前事务挂起。
"SUPPORTS":支持当前事务,如果当前没有事务,就以非事务方式执行。特点是随当前
调用者(事务/不是事务)的状态发生变化。
重点介绍"REQUIRED"和"REQUIRES_NEW"两个属性
eg:仍以买书为例,这次买多本书,买多本书(事务)可以看做{买一本书(事务)+ 买一本书(事务)+...+买一本书(事务)}
在代码上实现如下
买一本书的方法
@Service
public class OneBookServiceImpl implements OneBookService {
@Autowired //实例化
private BookDao bookDao;
/**
* 购买一本书有以下五个动作(查询或变更数据库)
*/
@Transactional(propagation=Propagation.REQUIRES_NEW) //可更改的地方
@Override
public void buyOneBook( String account , String isbn ) {
int price = this.bookDao.getBookPrice(isbn); //获取书的价格
int balance = this.bookDao.getUserBalance(account);
if(balance<price){ //查询余额是否充足
throw new RuntimeException("余额不足,请充值");
}
this.bookDao.updateUserBalance(account, price); //更新用户余额
int stock = this.bookDao.getBookStock(isbn);
if(stock<=0){ //查询库存
throw new RuntimeException("库存不足,请补充库存");
}
this.bookDao.updateBookStock(isbn); //更新库存数量
}}
买多本书的方法
@Service
public class MoreBookServiceImpl implements MoreBookService {
@Autowired //实例化
private OneBookService oneBookService;
@Override
public void buyMoreBook( String account , String[] isbns ) {
if(isbns!=null&&isbns.length>0){ //isbns有值
for(String isbn:isbns){
this.oneBookService.buyOneBook(account, isbn); //嵌套调用
}}}}
当buyOneBook方法的事务传播方式定义为"REQUIRED"时,新建一个事务,并将多个buyOneBook事务加入到这个事务中,
类似物理上的电路串联,只有当多个buyOneBook事务都能完成时,整个事务才可以完成,否则就会发生回滚。
在例子中就会产生要么把想要购买的书全部买走,要么放弃购买所有目标书。
当buyOneBook方法的事务传播方式定义为"REQUIRES_NEW"时,新建多个事务对应每个buyOneBook事务,
类似物理上的电路并联,当多个buyOneBook事务不能都完成时,可以完成的事务都可以完成,而不能完成的事务会发生回滚。
这样在例子中,能卖的起的书都会被买下来,而不能卖下来的书将放弃购买。
以上就是我的一些浅薄的关于Spring提供的事务管理器的了解。
部分内容摘自onyas前辈的博客Spring 之注解事务 @Transactional