首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Spring事务管理详解

事务的基本原理

Spring事务的本质其实就是数据库对事务的支持,使用JDBC的事务管理机制,就是利用java.sql.Connection对象完成对事务的提交,那在没有Spring帮我们管理事务之前,我们要怎么做。

事务是一系列的动作,一旦其中有一个动作出现错误,必须全部回滚,系统将事务中对数据库的所有已完成的操作全部撤消,滚回到事务开始的状态,避免出现由于数据不一致而导致的接下来一系列的错误。事务的出现是为了确保数据的完整性和一致性,在目前企业级应用开发中,事务管理是必不可少的。

与事务相关的理论知识

众所周知,事务有四大特性(ACID)

1.原子性(Atomicity)事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。

2.一致性(Consistency)事务在完成时,必须是所有的数据都保持一致状态。

3.隔离性(Isolation)并发事务执行之间无影响,在一个事务内部的操作对其他事务是不产生影响,这需要事务隔离级别来指定隔离性。

4.持久性(Durability)一旦事务完成,数据库的改变必须是持久化的。

在企业级应用中,多用户访问数据库是常见的场景,这就是所谓的事务的并发。事务并发所可能存在的问题:

1.脏读:一个事务读到另一个事务未提交的更新数据。

2.不可重复读:一个事务两次读同一行数据,可是这两次读到的数据不一样。

3.幻读:一个事务执行两次查询,但第二次查询比第一次查询多出了一些数据行。

4.丢失更新:撤消一个事务时,把其它事务已提交的更新的数据覆盖了。

我们可以在java.sql.Connection中看到JDBC定义了五种事务隔离级别来解决这些并发导致的问题:

TRANSACTION_NONE JDBC 驱动不支持事务

TRANSACTION_READ_UNCOMMITTED 允许脏读、不可重复读和幻读。

TRANSACTION_READ_COMMITTED 禁止脏读,但允许不可重复读和幻读。

TRANSACTION_REPEATABLE_READ 禁止脏读和不可重复读,单运行幻读。

TRANSACTION_SERIALIZABLE 禁止脏读、不可重复读和幻读。

隔离级别越高,意味着数据库事务并发执行性能越差,能处理的操作就越少。你可以通过conn.setTransactionLevel去设置你需要的隔离级别。

JDBC规范虽然定义了事务的以上支持行为,但是各个JDBC驱动,数据库厂商对事务的支持程度可能各不相同。

出于性能的考虑我们一般设置TRANSACTION_READ_COMMITTED就差不多了,剩下的通过使用数据库的锁来帮我们处理别的,关于数据库的锁这个之后再说。

了解了基本的JDBC事务,那有了Spring,在事务管理上会有什么新的改变呢?

有了Spring,我们再也无需要去处理获得连接、关闭连接、事务提交和回滚等这些操作,使得我们把更多的精力放在处理业务上。事实上Spring并不直接管理事务,而是提供了多种事务管理器。他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。

Spring事务管理

Spring事务管理的核心接口是PlatformTransactionManager

事务管理器接口通过getTransaction(TransactionDefinition definition)方法根据指定的传播行为返回当前活动的事务或创建一个新的事务,这个方法里面的参数是TransactionDefinition类,这个类就定义了一些基本的事务属性。

在TransactionDefinition接口中定义了它自己的传播行为和隔离级别

除去常量,主要的方法有:

Spring事务的传播属性

由上图可知,Spring定义了7个以PROPAGATION_开头的常量表示它的传播属性。

Spring事务的隔离级别

调用PlatformTransactionManager接口的getTransaction()的方法得到的是TransactionStatus接口的一个实现

TransactionStatus接口

主要的方法有:

可以看出返回的结果是一些事务的状态,可用来检索事务的状态信息。

配置事务管理器

介绍完Spring事务的管理的流程大概是怎么走的。接下来可以动手试试Spring是如何配置事务管理器的

例如我在spring-mybatis中配置的:

这配置不是唯一的,可以根据自己项目选择的数据访问框架灵活配置事务管理器

配置了事务管理器后,事务当然还是得我们自己去操作,Spring提供了两种事务管理的方式:编程式事务管理和声明式事务管理,让我们分别看看它们是怎么做的吧。

编程式事务管理

编程式事务管理我们可以通过PlatformTransactionManager实现来进行事务管理,同样的Spring也为我们提供了模板类TransactionTemplate进行事务管理,下面主要介绍模板类,我们需要在配置文件中配置

TransactionTemplate帮我们封装了许多代码,节省了我们的工作。下面我们写个单元测试来测测。

为了测试事务回滚,专门建了一张tbl_accont表,用于模拟存钱的一个场景。service层主要代码如下,后面会给出全部代码的github地址,有需要的朋友请移步查看。

BaseSeviceImpl

dao对应的sum方法

下面看看测试代码

当baseSevice.insert的第二个参数为false时,我们假设插入数据没有出现任何问题,测试结果如图所示:

当第二个参数为true时,insert会抛出一个异常,这是事务就应该回滚,数据前后不应该有变化,如图所示:

声明式事务管理

声明式事务管理有两种常用的方式,一种是基于tx和aop命名空间的xml配置文件,一种是基于@Transactional注解,随着Spring和Java的版本越来越高,大家越趋向于使用注解的方式,下面我们两个都说。

1.基于tx和aop命名空间的xml配置文件

配置文件

测试代码

事务正常执行结果截图

事务出现异常结果截图

2.基于@Transactional注解

这种方式最简单,也是最为常用的,只需要在配置文件中开启对注解事务管理的支持。

然后在需要事务管理的地方加上@Transactional注解,如:

rollbackFor属性指定出现Exception异常的时候回滚,遇到检查性的异常需要回滚,默认情况下非检查性异常,包括error也会自动回滚。

测试代码和上面那个一样

事务正常执行结果截图

事务出现异常结果截图

以上就是对Spring事务进行了详细的分析和代码示例。

觉得有用就打赏一下吧

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180618G1EWJZ00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券