您的位置:首页 > 编程语言 > Java开发

[置顶] Spring事务详解

2017-03-12 20:45 218 查看

背景

前些日子我司的DBA分享了关于MySql数据库的一些经验和技巧吧。但我突然发现,我还是保留着大学的一贯作风,上课不好好听讲。导致对于事务以及Spring的事务传播

机制还是不很了解。遂复习复习。

事务的简单回顾

什么是事务呢?我们如果将在数据库的修改前视为数据库的一种状态,那么在修改后是另一种状态。而我们的修改过程实际上就是让数据库的状态发生变化,而我们所做的这一系列的操作的集合就是事务了。

那么,对于一个正确的事务有什么样的要求呢?实际上

ISO/IEC制定了一个标准。那就是事务必须服从ACID原则。

ACID也就是原子性(atomicity)、一致性(consistency)、隔离性(isolation)、持久性(durability)的缩写。那么他们分别是什么意思呢?下面我们一起看看!

1、原子性:这个很好理解,我们回想起高中物理里面的原子是不可再分的就能知道,每一个事务就已经是最小的单位,不可再分。

2、一致性:事务的执行是保证数据库的状态能够正确的转变到另外一种状态。

3、隔离性:就是说事务在提交以前,数据库从一种状态到达另一种状态的过程还没有完城,此时的数据还未能提交,因此不能被读取。

4、持久性:这里的意思是说事务的结果将永久保存,不会在改变。就算事务之后数据库有了故障,不影响事务的结果。

事务的重要性

看完了上面对事务的简单回顾,我们接下来想一想事务为何如此重要!实际上,我们以银行转账的例子来看,我给我的媳妇转账一百元。



大家仔细的看上图,ATM机在我转账后一定回去更新数据库,此时,我的账户的钱的扣除以及我媳妇的账户的钱的增加一定在一个事务中。假设事务失败了,扣了钱却没加,额,去找银行,这可有点。。。因此,事务是很重要的概念。

Spring的事务支持

我们首先来看看事务的传统配法!声明式的配置方法如下:



这种的配置方法看上去是比较麻烦的,当然,我们下面就来看看基于注解的配置方法。

<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="shopDataSource"/>
</bean>


然后只需要在需要配置事务的方法上加上@Transactional注解就可以了。是不是很方便。当然,注意要去扫描相应的注解啊!

但是我们可以发现一个问题,那就是这样配置它的粒度是比较粗的,那如何细粒度的去配置呢?实际上,需要借助TransactionTemplate来实现。

具体配置如下:

<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="shopDataSource"/>
</bean>
<bean  id="template" class="org.springframework.transaction.support.TransactionTemplate">
<constructor-arg type="org.springframework.transaction.PlatformTransactionManager" ref="transactionManager"></constructor-arg>
</bean>


然后要做的事情就是将这个bean注入你需要事务管理的地方即可。

transactionTemplate.execute(new TransactionCallback<Integer>() {
@Override
public Integer doInTransaction(TransactionStatus status) {
return null;
}
});


如上,在接口的实现中写你的业务代码即可!

Spring的事务传播特性

接下来,我们要看的是Spring的事务传播特性。如下所示:

事务传播特性

Propagation.REQUIRED

方法运行时如果已经处在一个事务中,那么就加入到这个事务中,否则自己新建一个事务,REQUIRED是默认的事务传播特性

Propagation.NOT_SUPPORTED

如果方法没有关联到一个事务,容器不会为它开启一个事务,如果方法在一个事务中被调用,该事务会被挂起直到方法调用结束再继续执行

Propagation.REQUIRES_NEW

不管是否存在事务,该方法总会为自己发起一个新的事务,如果方法已经运行在一个事务中,则原有事务挂起,新的事务被创建

Propagation.MANDATORY

该方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务,如果在没有事务的环境下被调用,容器抛出异常

Propagation.SUPPORTS

该方法在某个事务范围内被调用,则方法成为该事务的一部分,如果方法在该事务范围内被调用,该方法就在没有事务的环境下执行

Propagation.NEVER

该方法绝对不能在事务范围内执行,如果在就抛出异常,只有该方法没有关联到任何事务,才正常执行

Propagation.NESTED

如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行,它只对DataSourceTransactionManager事务管理器有效

以上的内容,我就不在加以解释了。一般而言我们都采取的是默认的方式。

事务的隔离级别

一般而言,Spring采用的是数据库的默认级别,当然,还可以配置成别的形式。如下所示:

Spring事务的隔离级别

ISOLATION_DEFAULT: 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.

ISOLATION_READ_UNCOMMITTED: 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。

这种隔离级别会产生脏读,不可重复读和幻像读。

ISOLATION_READ_COMMITTED: 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据

ISOLATION_REPEATABLE_READ: 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。

它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。

ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。

除了防止脏读,不可重复读外,还避免了幻像读。

那么上面提到的脏读、不可重复读、幻读又是什么呢?

首先来说说脏读吧,还是以刚才我给我媳妇转账的例子来看。



A事务就是转账的事务,A事务采用多线程的方式去更新数据库。此时其更新了我媳妇的账户加了一百。另一个查询就读取了这个数据,但是我的账户减钱的操作并未完成。此时就发生了脏读。

其次,假如有一个查询的功能,此时他要查两次,这两次是在一个事务里面的。第一次查询时数据并未被修改,但是,随后数据被另一个事务修改并提交。第二次读到的数据就是修改后的数据。这就叫做不可重复读。

最后,幻读就是说事务并非独立执行的发生的现象。有一个事务包含A、B两个小事务。A事务修改了表中的所有记录,而B事务添加了一条记录。此时A事务如果去查看结果就会发现有一条数据并未修改。但是明明修改了。这就感觉像是幻觉一般。因此,被称为幻读。

总结

事务在我们实际的工作中非常重要,对于保持数据一致相当重要,本文从事物的概念、重要性、Sprign如何使用事务、事务的隔离级别等对事务做了深入的剖析。希望大家能有收获!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: