教你写框架系列之Spring第七讲事务概念讲解与Spring事务配置(二)

Posted by 田俊哲 on October 8, 2017

作者说

上一讲我们主要讲解了事务的四大特性以及四个隔离级别,系统性的了解了事务。在最后我们用一个买书的例子发现了事务的重要性。而本节我们将用Spring的声明式事务来为我们的需求添加事务。

Spring的声明式事务

java-javascript java-javascript

首先如上图所示,我们先在xml文件中先配置事务管理器(DataSourceTransactionManager),我们要创建DataSourceTransactionManager的实例,而实例需要往里面传入我们上面配置好的数据库连接池的DataSource。

除此之外,我们的Spring事务使用注解来配置,所以我们还要配置上图的事务注解驱动。

下面我们来对我们的purchase()方法加注解。

java-javascript

按照常规,余额不足会提示,但书的库存会减一,但我们增加这个注解后发现同样会提示余额不足,神奇的是我们书的库存并没有减一。这就是事务的作用。整个例子体现了事物的四个特性。原子一致隔离和持久。这就是我们使用Spring声明式事务进行注解配置的基本流程,但这远远不够,我们对事务注解添加多个属性,我们来了解一下这些属性。

属性一 propagation 事务传播属性。所谓的事务传播属性指的是当事务方法被另一个事务方法调用时,必须指定该如何传播,例如方法可能继续在现有的事务中运行,也可以开启一个新的事务,在自己的事务中运行。就好比小张和小田去操场打球,在操场遇见了小孙,小孙邀请小张和小田一起打球。这个时候小张和小田拒绝小孙属于一种传播行为,小张小田和小孙一起打球又是一种传播行为。 默认取值为 REQUIRED, 即使用调用方法的事务,即三人一起打球。

REQUIRES_NEW: 使用自己的事务, 调用的事务方法的事务被挂起,当运行到被调用的方法时我们大方法的事务要挂起,运行小方法的事务。

我们依然通过一个例子来讲解事务的传播行为。

现在AAA这个用户不在只购买一本书了,购买多本书,用list来装一起,如下图所示,checkout()方法里面遍历我们要买的书,然后调用我们刚才买书的方法purchase()这里我们checkout()方法添加了事务,purchase()同样添加了事务。

java-javascript

我们来修改一下数据库的信息,AAA手上有130,我们要买两本书,一本100,一本70。两种书各十本。在执行前我们来讲一下默认的传播行为。按照大的方法走,即执行的两次买书小方法的事务取消,由大方法的事务来管理。我们来运行一下方法。得到余额不足的提示,我们看数据库,两种书都还是十本,而且AAA手上还是原来的130.。我们来想一下啊,按照常规第一本书100块钱是可以买成功的,第二本事买失败了。由于我们使用的是大方法的事务来管理,所以第二个小方法执行失败,会回滚将第一个小方法也回滚到初始状态。AAA的钱没变, 书的库存也没变化。结论,默认情况下事务的传播行为为REQUIRED,取消小方法的事务,使用大方法的事务管理。

这里注意一下,我们的事务传播行为要在小方法里面去设置,而不是在大方法里面去设置。下面我们使用另一种事物的传播行为REQUIRES_NEW来看一下执行结果。

java-javascript

同样按照上面的方式配置我们的数据库,我们执行方法。结果却大不一样。依然会报一个余额不足的异常,但我们发现第一本书购买成功,因为数据库中库存减1了,而且我们发现用户的资金从原来的130变成了30。这就是我们的第二种传播行为。我们来想一下阿,大方法先执行,执行到小方法的时候大方法的事务挂起,小方法的事务开始接管,知道小方法执行完成,小事务挂起,大失误继续接管。这就是我们的REQUIRES_NEW传播行为。

我们刚刚讲了事务的第一个属性,传播行为。我们继续刚才的介绍,我们来介绍下一个事务属性。

第二个属性,isolation 设置隔离级别。具体的级别可以看我第一讲的讲述。我们这里常用的隔离级别为读已提交READ_COMMITTED

第三个属性 noRollbackFor RollbackFor对哪些异常不回滚和对哪些异常回滚。例如我们设置下图的属性。

java-javascript

我们对UserAccountException这个异常不回滚了,也就意味着我们进行买书操作时我们会从书库存中减一了,证明我们设置的属性起效果了。默认情况下我们不需要设置,默认的声明式事务对所有的运行时异常进行回滚。

第四个属性,readOnly 指定事务是否为只读,如果设置了,我们数据库就不会加锁,因为我们没有增删改的操作,这样数据库引擎得到优化,是我们查询速度更快。一般我们队get*方法都设置只读

第五个属性,timeout,使用timeout指定强制回滚之前事务可以占用的时间。假如我们设置timeout时间为2,如果我们这个事务方法在查询数据库这个过程中时间超过了两秒,我们就强制回滚了。不在进行数据库的交互。这样可以提高我们与数据库交互的性能。不让这个方法占用数据库过多的时间。

总结

下一讲我们将去除注解改使用xml的方式对方法配置事务,xml配置法是我们在做开发比较常用的方式。然后再对我们这几讲进行一个总结。

著作权声明

作者 田俊哲,首次发布于 Shmilyz Blog,本文原创,转载请保留以上链接!