1.概述

Java Transaction API,通常称为JTA,是用于管理 Java中的事务的API 。它允许我们以资源无关的方式启动,提交和回滚事务。

根据用于管理事务的底层实现,Spring中的事务策略可以分为两个主要部分:

  • 单连接器策略(相当于本地事务管理器) - 底层技术使用单连接器。例如,JDBC使用连接级事务、Hibernate以及JDO使用会话级事务。可以应用使用AOP和拦截器的声明式事务管理。
  • 多连接器策略(相当于全局事务管理器) - 底层技术具有使用多个连接器的能力。当有这方面需求时,JTA是最好的选择。此策略需要启用JTA的数据源实例。JBossTS、Atomikos、Bitronix都是开源的JTA实现。

JTA的真正强大之处在于它能够在单个事务中管理多个资源(如数据库,消息服务)。

在本文中,我们将从概念层面了解JTA,并了解业务代码通常是如何与JTA交互。

2.通用接口和分布式事务

JTA提供了对业务代码的事务控制(开始,提交和回滚)的抽象。

如果没有这种抽象,我们必须处理每种资源类型的各个API。

例如,我们需要处理JDBC资源。同样,JMS资源可能具有类似但不兼容的模型。

通过JTA,我们可以以一致和协调的方式管理不同类型的多种资源。

作为API,JTA定义了由事务管理器实现的接口和语义 。实现由Atomikos和Bitronix等库提供。

3.示例项目

本例子模拟了银行应用的一个非常简单的转账业务。我们有两个服务:银行账户服务BankAccountService 和操作行为审计服务AuditService,它们使用了两个不同的数据库。

数据库采用的是JAVA内置数据库HSQLDB,这些独立的数据库需要在事务开始,提交或回滚时进行协调。

JTA事务管理器采用Bitronix。

我们的示例项目使用Spring Boot来简化配置:

<dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.1.4.RELEASE</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jta-bitronix</artifactId></dependency><dependency><groupId>org.hsqldb</groupId><artifactId>hsqldb</artifactId></dependency>
</dependencies>

在服务启动时,建立2个数据源:accountDb,auditDb:

@Bean("dataSourceAccount")
public DataSource dataSource() throws Exception {return createHsqlXADatasource("jdbc:hsqldb:mem:accountDb");
}@Bean("dataSourceAudit")
public DataSource dataSourceAudit() throws Exception {return createHsqlXADatasource("jdbc:hsqldb:mem:auditDb");
}

在每个测试方法之前,我们使用脚本分别在每个库下创建表:ACCOUNT和AUDIT_LOG,并初始化一些数据:

ID BALANCE
a0000001 1000
a0000002 2000

4.声明性事务界定

在JTA中处理事务的第一种方法是使用@Transactional注解。

让我们用@Transactional注解服务方法 executeTranser()。 这表明事务管理器开始事务:

@Transactional
public void executeTransfer(String fromAccontId, String toAccountId,BigDecimal amount) {bankAccountService.transfer(fromAccontId, toAccountId, amount);auditService.log(fromAccontId, toAccountId, amount);BigDecimal balance = bankAccountService.balanceOf(fromAccontId);if (balance.compareTo(BigDecimal.ZERO) < 0) {throw new RuntimeException("余额不足!");}
}

这里 executeTranser()方法调用了2个不同的服务:AccountService和AuditService。这2个服务使用2个不同的数据库。

当 executeTransfer()返回时,该事务管理器识别出它是事务的结束,将作用于两个数据库。

在方法结束时,如果转账人的资金不足, executeTransfer()会检查帐户余额并抛出 RuntimeException异常。

4.1没有异常全部提交

场景1:当从a0000001账户给a0000002账户转账500元时,由于执行金额小于余额,一切正常。行为审计表里增加一条记录。

@Test
public void givenAnnotationTx_whenNoException_thenAllCommitted()throws Exception {tellerService.executeTransfer("a0000001", "a0000002",BigDecimal.valueOf(500));assertThat(accountService.balanceOf("a0000001")).isEqualByComparingTo(BigDecimal.valueOf(500));assertThat(accountService.balanceOf("a0000002")).isEqualByComparingTo(BigDecimal.valueOf(2500));TransferLog lastTransferLog = auditService.lastTransferLog();assertThat(lastTransferLog).isNotNull();assertThat(lastTransferLog.getFromAccountId()).isEqualTo("a0000001");assertThat(lastTransferLog.getToAccountId()).isEqualTo("a0000002");assertThat(lastTransferLog.getAmount()).isEqualByComparingTo(BigDecimal.valueOf(500));
}

4.2出现异常进行回滚

场景2:当从a0000002账户给a0000001账户转账10000元时,由于执行金额大于余额,抛出异常。两个数据库进行回滚,账户余额不变,行为审计表没有数据。

@Test
public void givenAnnotationTx_whenException_thenAllRolledBack()throws Exception {assertThatThrownBy(() -> {tellerService.executeTransfer("a0000002", "a0000001",BigDecimal.valueOf(100000));}).hasMessage("余额不足!");assertThat(accountService.balanceOf("a0000001")).isEqualByComparingTo(BigDecimal.valueOf(1000));assertThat(accountService.balanceOf("a0000002")).isEqualByComparingTo(BigDecimal.valueOf(2000));assertThat(auditService.lastTransferLog()).isNull();
}

5.编程性事务界定

另一种控制JTA事务的方法是通过调用 javax.transaction.UserTransaction以编程方式实现。

public void executeTransferProgrammaticTx(String fromAccontId,String toAccountId, BigDecimal amount) throws Exception {userTransaction.begin();bankAccountService.transfer(fromAccontId, toAccountId, amount);auditService.log(fromAccontId, toAccountId, amount);BigDecimal balance = bankAccountService.balanceOf(fromAccontId);if (balance.compareTo(BigDecimal.ZERO) < 0) {userTransaction.rollback();throw new RuntimeException("余额不足!");} else {userTransaction.commit();}
}

在我们的示例中,begin()方法启动了一个新事务。如果余额验证失败,调用rollback(),它将回滚两个数据库。否则,调用commit() 会将更改提交给两个数据库。

需要注意的是 commit()和 rollback()都 结束当前事务。

6.总结

在本文中,我们讨论了JTA试图解决的问题。通过示例工程说明了使用注释和编程方式来控制事务,涉及需要在单个事务中协调2个事务资源。

示例完整代码可以在GitHub上找到。

Spring JTA分布式事务实现相关推荐

  1. Spring MyBatis Atomikos 实现JTA分布式事务

    Spring+MyBatis+Atomikos实现JTA分布式事务 项目中需要同时操作两个数据库,对两个数据库中的表同时做变更时就需要控制事务,要么全部成功,要么全部失败. Atomikos是一个开源 ...

  2. [译] Spring 的分布式事务实现-使用和不使用XA — 第三部分

    原文地址:Distributed transactions in Spring, with and without XA - Part III 原文作者:David Syer 译文出自:掘金翻译计划 ...

  3. jta分布式事务简单使用

    个人理解总结: jta分布式事务,其实与普通的spring事务配置没多大区别,最主要的是配置不同的mapper使用不同的的数据源,然后再配atomikos和spring的jta事务管理器进行管理(sp ...

  4. 事务 | Spring Cloud 分布式事务管理(二)2pc/3pc

    Spring Cloud 分布式事务管理(二)2pc/3pc 上一篇 Spring Cloud 分布式事务管理 上一章,讲到了微服务带来的优点和缺点以及分布式事务的不确定性.这节说一下2pc/3pc ...

  5. 使用Atomikos Transactions Essentials实现多数据源JTA分布式事务--转载

    原文:http://www.ite/topic/122700 9.17 update:使用NonXADataSourceBean. Mysql在5.0版本和Connecter/J5.0版本后提供了XA ...

  6. SpringBoot之JPA框架下如何使用JTA——分布式事务解决方案

    以前,笔者写过一篇博客,支付宝DTS方案,当然,只是仅仅是简单讨论了下分布式事务的解决方案.PS:笔者看了下相关评论,发现由于太简单,被不少人Diss了一通. 最近,笔者在自己的工程上,试图一次性解决 ...

  7. Java JDBC事务与JTA分布式事务

    Java事务的类型有三种:JDBC事务.JTA(Java Transaction API)事务.容器事务. 常见的容器事务如Spring事务,容器事务主要是J2EE应用服务器提供的,容器事务大多是基于 ...

  8. spring boot 分布式事务解决方案

    对比LCN和saga(华为apache孵化器项目) ,LCN使用代理连接池封装补偿方法,saga需要手工写补偿方法,相对来说LCN使用更加方便. 参考官方地址: https://github.com/ ...

  9. 分布式事务解决方案——基于Atomikos的实现

    声明:以下关于"JTA规范事务模型"."Spring JTA分布式事务的实现"等内容均来源于其他大佬的博客内容,并已经表明出处. 1.JTA规范事务模型   J ...

最新文章

  1. Glide 的基本使用
  2. shell把字符串中的字母去掉,只保留数字
  3. Matlab下 IIR 滤波器实现(Simulink仿真和C语言实现)
  4. 科技情报研究所工资_我们所说的情报是什么?
  5. 评估一个垃圾收集(GC)
  6. 大白话Pyramid Vision Transformer
  7. sift算法的主要步骤
  8. 编译android源码 太慢,记编译Android源码的经历
  9. c语言编程 猜字游戏
  10. 【线性代数】P8 逆矩阵矩阵方程以及逆矩阵的性质
  11. 中国氨基酸表面活性剂市场前景展望与发展建议分析报告2022-2028年
  12. 华为路由器isis配置实例_华为-ISIS路由协议(原理+配置实操)
  13. 还在找面试题?不要盲目找了,这份 Java 面试通关手
  14. MacPS安装服务器当前无响应,最新:M1芯片Mac安装Photoshop2021闪退/卡在启动屏页解决办法...
  15. 淘宝店铺装修教程之下载淘宝视频及分析视频地址中的高逼格信息
  16. Git:git-pull 的用法总结
  17. Linux 之 zsh
  18. HTML5中设置自适应设备头的各个参数及意义
  19. 腾讯游戏学院专家实例剖析:如何优化休闲游戏的美术风格?
  20. 全平台安卓源码预置GMS方法 / 全平台安卓源码预置谷歌服务方法

热门文章

  1. 歌曲名、艺术家名显示乱码
  2. NIO详解(六):Java堆外内存
  3. 判断pdf、word文档、图片等文件类型(格式)、大小的简便方法
  4. 一次完整的HTTP事务是怎样一个过程?
  5. Web h5开发 初学总结
  6. 远程锁定计算机,远程关机
  7. 流言飞鱼GhostXP_SP3活力版V18.0(软件管理器)
  8. 前端小制作 Part.1---鼠标悬停导航栏特效
  9. latex±号_latex中数学符号
  10. 用falsk写一个简单的接口