基于可靠消息方案的分布式事务(二):Java中的事务

前言:在上一篇文章

基于可靠消息方案的分布式事务:Lottor介绍

中介绍了常见的分布式事务的解决方案以及笔者基于可靠消息方案实现的分布式事务组件Lottor的原理,并展示了应用的控制台管理。在正式介绍Lottor的具体实现之前,本文首先将会介绍Java中的事务管理,重点介绍Spring的事务管理。

Java 事务的类型

事务管理是应用系统开发中必不可少的一部分。Java事务的类型有三种:JDBC事务、JTA(Java Transaction API)事务、容器事务。 常见的容器事务如Spring事务,容器事务主要是J2EE应用服务器提供的,容器事务大多是基于JTA完成,这是一个基于JNDI的,相当复杂的API实现。首先介绍J2EE开发中的两个事务:JDBC事务和JTA事务。

JDBC 事务

JDBC的一切行为包括事务是基于一个Connection的,在JDBC中是通过Connection对象进行事务管理。在JDBC中,常用的和事务相关的方法是: setAutoCommit、commit、rollback等。

默认情况下,当我们创建一个数据库连接时,会运行在自动提交模式(Auto-commit)下。这意味着,任何时候我们执行一条SQL完成之后,事务都会自动提交。所以我们执行的每一条SQL都是一个事务,并且如果正在运行DML或者DDL语句,这些改变会在每一条SQL语句结束的时存入数据库。有时候我们想让一组SQL语句成为事务的一部分,那样我们就可以在所有语句运行成功的时候提交,并且如果出现任何异常,这些语句作为事务的一部分,我们可以选择将其全部回滚。

上面的代码实现了一个简单的插入账号和地址信息的功能,通过事务来控制插入操作,要么都提交,要么都回滚。

JDBC为使用Java进行数据库的事务操作提供了最基本的支持。通过JDBC事务,我们可以将多个SQL语句放到同一个事务中,保证一个 JDBC 事务不能跨越多个数据库!所以,如果涉及到多数据库的操作或者分布式场景,JDBC事务就无能为力了。

JTA 事务

通常,JDBC事务就可以解决数据的一致性等问题,鉴于他用法相对简单,所以很多人关于Java中的事务只知道有JDBC事务,或者有人知道框架中的事务(比如Hibernate、Spring)等。但是,由于JDBC无法实现分布式事务,而如今的分布式场景越来越多,所以,JTA事务就应运而生。

JTA(Java Transaction API),Java事务API允许应用程序执行分布式事务,也就是说事务可以访问或更新两个或更多网络上的计算机资源。JTA指定事务管理器和分布式事务系统中涉及的各方之间的标准Java接口:应用程序,应用程序服务器和控制对受事务影响的共享资源的访问的资源管理器。一个事务定义了完全成功或根本不产生结果的逻辑工作单元。

Java 事务编程接口(JTA:Java Transaction API)和 Java 事务服务 (JTS;Java Transaction Service) 为 J2EE 平台提供了分布式事务服务。分布式事务(Distributed Transaction)包括事务管理器(Transaction Manager)和一个或多个支持 XA 协议的资源管理器 ( Resource Manager )。我们可以将资源管理器看做任意类型的持久化数据存储;事务管理器承担着所有事务参与单元的协调与控制。JTA 事务有效的屏蔽了底层事务资源,使应用可以以透明的方式参入到事务处理中;但是与本地事务相比,XA 协议的系统开销大,在系统开发过程中应慎重考虑是否确实需要分布式事务。若确实需要分布式事务以协调多个事务资源,则应实现和配置所支持 XA 协议的事务资源,如 JMS、JDBC 数据库连接池等。

JTA里面提供了 java.transaction.UserTransaction ,里面定义了下面几个方法:

begin()- 开始一个分布式事务,(在后台 TransactionManager 会创建一个 Transaction 事务对象并把此对象通过 ThreadLocale 关联到当前线程上 )

commit()- 提交事务(在后台 TransactionManager 会从当前线程下取出事务对象并把此对象所代表的事务提交)

rollback()- 回滚事务(在后台 TransactionManager 会从当前线程下取出事务对象并把此对象所代表的事务回滚)

getStatus()- 返回关联到当前线程的分布式事务的状态 (Status 对象里边定义了所有的事务状态)

setRollbackOnly()- 标识关联到当前线程的分布式事务将被回滚

值得注意的是,不是使用了UserTransaction就能把普通的JDBC操作直接转成JTA操作,JTA对DataSource、Connection和Resource 都是有要求的,只有符合XA规范,并且实现了XA规范的相关接口的类才能参与到JTA事务中来。

关于XA规范,读者可以根据前一篇文章进行补充,目前主流的数据库都支持XA规范。使用的流程如下:

要想使用用 JTA 事务,那么就需要有一个实现 javax.sql.XADataSource 、 javax.sql.XAConnection 和 javax.sql.XAResource 接口的 JDBC 驱动程序。一个实现了这些接口的驱动程序将可以参与 JTA 事务。一个 XADataSource 对象就是一个 XAConnection 对象的工厂。XAConnection 是参与 JTA 事务的 JDBC 连接。

要使用JTA事务,必须使用XADataSource来产生数据库连接,产生的连接为一个XA连接。

XA连接(javax.sql.XAConnection)和非XA(java.sql.Connection)连接的区别在于:XA可以参与JTA的事务,而且不支持自动提交。

除了数据库,还有JBoss、JMS的消息中间件ActiveMQ等很多组件都是遵守XA协议,2PC和3PC也是符合XA规范的。具体JTA更多的内容,本文不再展开,后面有机会专门深入介绍JTA事务。

Spring 事务管理

Spring支持编程式事务管理和声明式事务管理两种方式。Spring所有的事务管理策略类都继承自接口,类图如下:

PlatformTransactionManager类图

其中,方法根据指定的传播行为返回当前活动的事务或创建一个新的事务,这个方法里面的参数是类,这个类定义了一些基本的事务属性。 其他两个方法,分别是提交和回滚,传入事务状态值。

事务属性

其实通过 可以了解事务属性的定义。事务属性描述了事务策略如何应用到方法上,事务属性包含5个方面:

传播行为

隔离级别

回滚规则

事务超时

是否只读

Spring 事务传播属性

传播行为定义了客户端与被调用方法之间的事务边界,即传播规则回答了这样的一个问题,新的事务应该被启动还是挂起,或者方法是否要在事务环境中运行。Spring定义了7个以开头的常量表示它的传播属性。在中定义了如下的常量:

Spring 隔离级别

隔离级别定义了一个事务可能受其他并发事务影响的程度。多事务并发可能会导致脏读、幻读、不可重复读等各种读现象。在中定义了如下的常量:

是否只读

如果事务只对后端的数据库进行读操作,数据库可以利用事务ID只读特性来进行一些特定的优化。通过将事务设置为只读,你就可以给数据库一个机会,让他应用它认为合适的优化措施。因为是否只读是在事务启动的时候由数据库实施的,所以只有对那些具备可能启动一个新事务的传播行为(,,)的方法来说,才有意义。

事务超时

为了使应用程序很好地运行,事务不能运行太长时间。因为超时时钟会在事务开始时启动,所以只有对那些具备可能启动一个新事务的传播行为(同上几种)的方法来说,才有意义。默认设置为底层事务系统的超时值,如果底层数据库事务系统没有设置超时值,那么就是none,没有超时限制。

事务回滚

事务回滚规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下,事务只有在遇到运行时期异常才回滚,而在遇到检查型异常时不会回滚。就是抛出的异常为的子类(Errors也会导致事务回滚),而抛出checked异常则不会导致事务回滚。可以明确的配置在抛出那些异常时回滚事务,包括checked异常。也可以明确定义那些异常抛出时不回滚事务。还可以通过编程的方法来指示一个事务必须回滚,在调用完后所能执行的唯一操作就是回滚。

配置 Spring 事务管理器

Spring中使用xml进行如下的配置即可:

如果是Spring Boot,我们只需要设置好即可,会自动配置好。

配置了事务管理器后,事务当然还是得我们自己去操作,Spring提供了两种事务管理的方式:编程式事务管理和声明式事务管理,下面分别看一下如何应用。

编程式事务管理

编程式事务管理我们可以通过实现来进行事务管理,同样的Spring也为我们提供了模板类进行事务管理,下面主要介绍模板类。

模板类

我们需要在配置文件中配置:

帮我们封装了许多代码,节省了我们的工作。专门建了一张account表,用于模拟存钱的一个场景。

对于抛出Exception类型的异常且需要回滚时,需要捕获异常并通过调用status对象的方法告知事务管理器当前事务需要回滚。

声明式事务管理

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

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

基于@Transactional注解

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

而Spring boot启动类必须要开启事务,而开启事务用的注解就是。

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

总结

本文主要介绍了Java事务的类型:JDBC事务、JTA(Java Transaction API)事务、容器事务。简要介绍了JDBC事务和JTA事务,详细介绍了Spring的事务管理,Spring对事务管理是通过事务管理器来实现的。Spring提供了许多内置事务管理器实现,如数据源事务管理器、集成JPA的事务管理等。分别介绍了Spring事务属性包含的5个方面,以及Spring提供的两种事务管理的方式:编程式事务管理和声明式事务管理。其中声明式的事务管理,最为便捷、使用的更多。通过本文的介绍,希望读者在接触分布式事务时,首先对Java中的事务能够熟悉。JTA事务时,其实也引出了分布式事务的相关概念,对应2PC和3PC的XA规范。

参考

http://www.hollischuang.com/archives/1489

https://www.ibm.com/developerworks/cn/java/j-lo-jta/

http://www.hollischuang.com/archives/1658

https://blog.csdn.net/donggua3694857/article/details/69858827

END

【推荐书籍】

扫码购买

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

扫码关注云+社区

领取腾讯云代金券