Java分布式事务(九)

文章目录

    • 🔥XA强一致性分布式事务实战_Atomikos介绍
    • 🔥XA强一致性分布式事务实战_业务说明
    • 🔥XA强一致性分布式事务实战_项目搭建
    • 🔥XA强一致性分布式事务实战_多数据源实现
    • 🔥XA强一致性分布式事务实战_业务层实现

🔥XA强一致性分布式事务实战_Atomikos介绍

在这里插入图片描述

简单介绍
Atomikos(https://www.atomikos.com/),其实是一家公司的名字,提供了基于 JTA规范的XA分布式事务TM的实现 。其旗下最著名的产品就是事务管理器。

在这里插入图片描述

产品分两个版本:
⭐TransactionEssentials:开源的免费产品;
⭐ExtremeTransactions:上商业版,需要收费。

这两个产品的关系如下图所示:
在这里插入图片描述

ExtremeTransactions 在 TransactionEssentials 的基础上额外提供了以下功能:
⭐支持 TCC:这是一种柔性事务
⭐支持通过 RMI、IIOP、SOAP 这些远程过程调用技术,进行事务传播。
⭐事务日志云存储,云端对事务进行恢复,并且提供了完善的管理后台。

什么是JTA
在这里插入图片描述
Java事务API(JTA:Java Transaction API)和它的同胞Java事务服务(JTS:Java Transaction Service),为J2EE平台提供了分布式事务服务(distributed transaction)的能力。
在这里插入图片描述

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

🔥XA强一致性分布式事务实战_业务说明

场景介绍
本案例使用Atomikos框架实现XA强一致性分布式事务,模拟跨库转账的业务场景。不同账户之间的转账操作通过同一个项目程序完成。
在这里插入图片描述

说明:
转账服务不会直接连接数据库进行转账操作,而是通过Atomikos框架对数据库连接进行封装,通过Atomikos框架操作不同的数据库。由于Atomikos框架内部实现了XA分布式事务协议,因此转账服务的逻辑处理不用关心分布式事务是如何实现的,只需要关注具体的业务逻辑。

框架选型

框架名字版本
MySQL5.7
JDK1.8
微服务框架SpringBoot 2.6.3
分布式事务框架Atomikos
持久层框架Mybatis plus

user_account数据表结构
在这里插入图片描述

设计完数据表后,在192.168.66.100服务器创建2个数据库,分别为tx-xa-01和tx-xa-02,分别在2个数据库中创建转出金额数据库。

DROP TABLE IF EXISTS user_account;
CREATE TABLE user_account (
  account_no varchar(64) CHARACTER SET utf8
COLLATE utf8_bin NOT NULL COMMENT '账户编号'
,
  account_name varchar(64) CHARACTER SET utf8
COLLATE utf8_bin NULL DEFAULT NULL COMMENT '账户名称',
  account_balance decimal(10, 2) NULL DEFAULT
NULL COMMENT '账户余额',
  PRIMARY KEY (account_no) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE
= utf8_bin ROW_FORMAT = Dynamic;

添加数据
tx-xa-01库中添加数据。

INSERT INTO `user_account` VALUES ('1001',
'张三', 10000.00);

tx-xa-02库中添加数据。

INSERT INTO `user_account` VALUES ('1002',
'李四', 10000.00);

🔥XA强一致性分布式事务实战_项目搭建

创建atomikos-xa项目
在这里插入图片描述

创建依赖

    <dependency>
          
<groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starterweb</artifactId>
        </dependency>
      <dependency>
          
<groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starterjta-atomikos</artifactId>
        </dependency>
        <!-- druid连接池依赖组件-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-bootstarter</artifactId>
            <version>1.1.22</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-bootstarter</artifactId>
        </dependency>
        <dependency>
          
<groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>   

编写配置文件

server:
 port: 6003
spring:
 autoconfigure:
    #停用druid连接池的自动配置
   exclude:
com.alibaba.druid.spring.boot.autoconfigure.Dru
idDataSourceAutoConfigure
 datasource:
    #选用druid的XADataSource数据源,因为这个数据源支
持分布式事务管理
   type:
com.alibaba.druid.pool.xa.DruidXADataSource
    #以下是自定义字段
   dynamic:
     primary: master
     datasource:
       master:
         url:
jdbc:mysql://192.168.66.102:3306/tx-xa-01?
useUnicode=true&characterEncoding=utf-
8&allowMultiQueries=true&useSSL=false&zeroDateT
imeBehavior=convertToNull&serverTimezone=Asia/S
hanghai&autoReconnect=true
         username: root
         password: 123456
         driver-class-name:
com.mysql.jdbc.Driver
       slave:
        url:
jdbc:mysql://192.168.66.102:3306/tx-xa-02?
useUnicode=true&characterEncoding=utf-
8&allowMultiQueries=true&useSSL=false&zeroDateT
imeBehavior=convertToNull&serverTimezone=Asia/S
hanghai&autoReconnect=true
         username: root
         password: 123456
         driver-class-name:
com.mysql.jdbc.Driver
       validation-query: SELCET 1

编写主启动类

@Slf4j
@SpringBootApplication
@EnableTransactionManagement(proxyTargetClass = true)
public class TxXaStarter {
    public static void main(String[] args){
      
SpringApplication.run(TxXaStarter.class,args);
        log.info("*************** TxXaStarter
*********");
   }
}

🔥XA强一致性分布式事务实战_多数据源实现

在这里插入图片描述

创建第一个数据源的配置类DBConfig1

@Data
@ConfigurationProperties(prefix =
"spring.datasource.dynamic.datasource.master")
public class DBConfig1 {
    private String url;
    private String username;
    private String password;
    private String dataSourceClassName;
}

创建第二个数据源的配置类DBConfig2

@Data
@ConfigurationProperties(prefix =
"spring.datasource.dynamic.datasource.slave")
public class DBConfig2 {
    private String url;
    private String username;
    private String password;
    private String dataSourceClassName;
}

创建持久层接口
分别在com.it.mapper1包和com.it.mapper2包下
创建UserAccount1Mapper接口和UserAccount2Mapper接口。

public interface UserAccount1Mapper extends
BaseMapper<UserAccount> {
}
public interface UserAccount2Mapper extends
BaseMapper<UserAccount> {
}

创建MyBatisConfig1类
MyBatisConfig1类的作用是整合Atomikos框架,读取DBConfig1类中的信息,实现数据库连接池,最终通过Atomikos框架的数据库连接池连接数据库并操作。

@Configuration
@MapperScan(basePackages =
"com.it.mapper1", sqlSessionTemplateRef
= "masterSqlSessionTemplate")
public class MyBatisConfig1 {
    @Bean(name = "masterDataSource")
    public DataSource
masterDataSource(DBConfig1 dbConfig1) {
        AtomikosDataSourceBean sourceBean = new AtomikosDataSourceBean();
      
sourceBean.setUniqueResourceName("masterDataSource");
      
sourceBean.setXaDataSourceClassName(dbConfig1.getDataSourceClassName());
        sourceBean.setTestQuery("select 1");
    sourceBean.setBorrowConnectionTimeout(3);
        MysqlXADataSource dataSource = new
MysqlXADataSource();
      
dataSource.setUser(dbConfig1.getUsername());
      
dataSource.setPassword(dbConfig1.getPassword()
);
        dataSource.setUrl(dbConfig1.getUrl());
        sourceBean.setXaDataSource(dataSource);
        return sourceBean;
   }
    @Bean(name = "masterSqlSessionFactory")
    public SqlSessionFactory
masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource) throws Exception
{
        MybatisSqlSessionFactoryBean
sqlSessionFactoryBean = new
MybatisSqlSessionFactoryBean();
      
sqlSessionFactoryBean.setDataSource(dataSource
);
        return
sqlSessionFactoryBean.getObject();
   }
    @Bean(name = "masterSqlSessionTemplate")
    public SqlSessionTemplate
masterSqlSessionTemplate(@Qualifier("masterSqlS
essionFactory") SqlSessionFactory
sqlSessionFactory){
        return new
SqlSessionTemplate(sqlSessionFactory);
   }
}

创建MyBatisConfig2类

MyBatisConfig2类的作用与MyBatisConfig1类的作用相似,只不过MyBatisConfig2类读取的是DBConfig2类中的信息,封装的是整合了Atomikos框架的另一个数据源的数据库连接池,通过连接池连接数据库并操作。

@Configuration
@MapperScan(basePackages =
"com.it.mapper2", sqlSessionTemplateRef
= "slaveSqlSessionTemplate")
public class MyBatisConfig2 {
    @Bean(name = "slaveDataSource")
    public DataSource slaveDataSource(DBConfig2 dbConfig2) {
        AtomikosDataSourceBean sourceBean = new AtomikosDataSourceBean();
      
sourceBean.setUniqueResourceName("slaveDataSource");
      
sourceBean.setXaDataSourceClassName(dbConfig2.
getDataSourceClassName());
        sourceBean.setTestQuery("select 1");
      
sourceBean.setBorrowConnectionTimeout(3);
        MysqlXADataSource dataSource = new
MysqlXADataSource();
      
dataSource.setUser(dbConfig2.getUsername());
      
dataSource.setPassword(dbConfig2.getPassword()
);
        dataSource.setUrl(dbConfig2.getUrl());
        sourceBean.setXaDataSource(dataSource);
        return sourceBean;
   }
    @Bean(name = "slaveSqlSessionFactory")

    public SqlSessionFactory
slaveSqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource) throws Exception {
        MybatisSqlSessionFactoryBean
sqlSessionFactoryBean = new
MybatisSqlSessionFactoryBean();
      
sqlSessionFactoryBean.setDataSource(dataSoure
);
        return
sqlSessionFactoryBean.getObject();
   }
    @Bean(name = "slaveSqlSessionTemplate")
    public SqlSessionTemplate
slaveSqlSessionTemplate(@Qualifier("slaveSqlSessionFactory") SqlSessionFactory
sqlSessionFactory){
        return new
SqlSessionTemplate(sqlSessionFactory);
   }
}

🔥XA强一致性分布式事务实战_业务层实现

项目的业务逻辑层主要实现具体的跨库转账的业务逻辑,由于具体的XA跨库分布式事务是由Atomikos框架内部实现的,因此在业务逻辑层处理跨库转账的逻辑时,就像操作本地数据库一样简单。

创建UserAccount类

@Data
@TableName("user_account")
@AllArgsConstructor
@NoArgsConstructor
public class UserAccount implements
Serializable {
    private static final long serialVersionUID = 6909533252826367496L;
    /**
     * 账户编号
     */
    @TableId
    private String accountNo;
    /**
     * 账户名称
     */
    private String accountName;
    /**
     * 账户余额
     */
    private BigDecimal accountBalance;
}

创建UserAccountService接口

public interface UserAccountService {
      /**
     * 跨库转账
     * @param sourceAccountNo 转出账户
     * @param targetSourceNo 转入账户
     * @param bigDecimal 金额
     */
   void transferAccounts(String sourceAccountNo, String targetSourceNo,
BigDecimal transferAmount);
}

实现UserAccountService接口

package com.it.service.impl;
import com.it.entity.UserAccount;
import com.it.mapper1.UserAccountMapper1;
import com.it.mapper2.UserAccountMapper2;
import com.it.service.IUserAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import
org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
/**
* <p>
* 服务实现类
* </p>
*
* @author itbaizhan
* @since 05-13
*/
@Service
public class UserAccountServiceImpl  implements
IUserAccountService {
    @Autowired
    private UserAccountMapper1
userAccountMapper1;
    @Autowired
    private UserAccountMapper2
userAccountMapper2;
  /**
     * 跨库转账
     * @param sourceAccountNo 源账户
     * @param targetSourceNo 目标账户
     * @param bigDecimal 金额
     */
    @Transactional
    @Override
     public void transofer(String
sourceAccountNo, String targetSourceNo,
BigDecimal bigDecimal) {
        // 1. 查询原账户
        UserAccount sourceUserAccount =
userAccountMapper1.selectById(sourceAccountNo);
        // 2. 查询目标账户
        UserAccount targetUserAccount =
userAccountMapper2.selectById(targetSourceNo);
        // 3. 判断转入账户和转出账户是否为空
        if (sourceAccountNo != null &&
targetUserAccount != null){
            // 4. 判断转出账户是否余额不足
            if
(sourceUserAccount.getAccountBalance().compare To(bigDecimal) < 0){
                throw  new RuntimeException("余额不足");
           }
            // 5.更新金额
          
sourceUserAccount.setAccountBalance(sourceUserAccount.getAccountBalance().subtract(bigDecimal));
            // 6.张三账户减金额
          
userAccountMapper1.updateById(sourceUserAccount);
            System.out.println(10/0);
  // 7.更新金额
          
targetUserAccount.setAccountBalance(targetUserAccount.getAccountBalance().add(bigDecimal));
            // 8.张三账户减金额
          
userAccountMapper2.updateById(targetUserAccount);
       }
   }
}           

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/1812.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JS判断是否为base64字符串如何转换为图片src格式

需求背景 &#xff1a; 如何判断后端给返回的 字符串 是否为 base-64 位 呢 &#xff1f; 以及如果判断为是的话&#xff0c;如何给它进行转换为 img 标签可使用的那种 src 格式 呢 &#xff1f; 1、判断字符串是否为 base64 以下方法&#xff0c;可自行挨个试试&#xff0c;…

蓝桥杯倒计时 | 倒计时20天

作者&#x1f575;️‍♂️&#xff1a;让机器理解语言か 专栏&#x1f387;&#xff1a;蓝桥杯倒计时冲刺 描述&#x1f3a8;&#xff1a;蓝桥杯冲刺阶段&#xff0c;一定要沉住气&#xff0c;一步一个脚印&#xff0c;胜利就在前方&#xff01; 寄语&#x1f493;&#xff1a…

第十四届蓝桥杯三月真题刷题训练——第 16 天

目录 第 1 题&#xff1a;英文字母 问题描述 输入格式 输出格式 样例输入 1 样例输出 1 样例输入 2 样例输出 2 评测用例规模与约定 运行限制 代码&#xff1a; 第 2 题&#xff1a;单词分析 题目描述 输入描述 输出描述 输入输出样例 运行限制 数组代码&…

【MySQL】聚合查询

目录 1、前言 2、插入查询结果 3、聚合查询 3.1 聚合函数 3.1.1 count 3.1.2 sum 3.1.3 avg 3.1.4 max 和 min 4、GROUP BY 子句 5、HAVING 关键字 1、前言 前面的内容已经把基础的增删改查介绍的差不多了&#xff0c;也介绍了表的相关约束&#xff0c; 从本期开始…

C语言实现队列(Push Pop Size Front EmptyBack)

队列是一个重要的数据结构&#xff0c;他的特性是先进先出&#xff0c;所以由于这个特性&#xff0c;队列只有一个入口和一个出口&#xff0c;所以只有push和pop 下面我们看一下他如何实现 首先我们来看一下他的结构体 这里我们看到我们定义了两个结构体&#xff0c;其中一个…

关于多层板,你了解多少?

01 前言 大家好&#xff0c;我是张巧龙。好久没写原创了&#xff0c;记得之前刚接触PCB时&#xff0c;还在用腐蚀单层板&#xff0c;类似这种。 慢慢随着电子产品功能越来越多&#xff0c;产品越来越薄&#xff0c;对PCB设计要求越来越高了&#xff0c;复杂程度也随之增加。因此…

第十四届蓝桥杯三月真题刷题训练——第 17 天

目录 第 1 题&#xff1a;ASC 运行限制 代码&#xff1a; 第 2 题&#xff1a;递增三元组_双指针_long 输出描述 输入输出样例 运行限制 代码&#xff1a; 第 3 题&#xff1a;环境治理 代码&#xff1a; 第 4 题&#xff1a;小球称重 代码&#xff1a; 第 1 题&a…

小白学Pytorch系列--Torch API (7)

小白学Pytorch系列–Torch API (7) Comparison Ops allclose 此函数检查输入和其他是否满足条件&#xff1a; >>> torch.allclose(torch.tensor([10000., 1e-07]), torch.tensor([10000.1, 1e-08])) False >>> torch.allclose(torch.tensor([10000., 1e-…

48天强训 Day1 JavaOj

48天强训 & Day1 & JavaOj 1. 编程题1 - 组队竞赛 组队竞赛_牛客笔试题_牛客网 (nowcoder.com) 1.1 读题 1.2 算法思想基础 我们应该尽量的让每一个队伍的中间值都最大化~我们应该尽量的让每一个队伍的最小值都足够小~前33%的不应该都作为每个队伍的最大值~ 接下来…

LeetCode算法 不同路径 和 不同路径II C++

目录题目 不同路径参考答案题目 不同路径II参考答案题目 不同路径 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finis…

Git 入门最佳实践

Git 入门最佳实践 前言 Git简介 实用主义 深入探索 总结 参考资料 前言 Git 是程序员学习和工作都离不开发工具&#xff0c;今天和大家分享 Git 常用命令总结。 Git简介 Git 是一种分布式版本控制系统&#xff0c;它可以不受网络连接的限制&#xff0c;加上其它众多优…

【Java】UDP网络编程

文章目录前言DatagramSocketDatagramPacket注意事项与区别代码演示前言 UDP&#xff08;user datagram protocol&#xff09;的中文叫用户数据报协议&#xff0c;属于传输层。 UDP是面向非连接的协议&#xff0c;它不与对方建立连接&#xff0c;而是直接把我要发的数据报发给对…

html+css 实现 熊猫样式

效果 html代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv"X-UA-Compatible"…

ThinkPHP01:数据库和模型

ThinkPHP01&#xff1a;数据库和模型一、开启调试模式二、配置文件三、URL解析四、数据库五、模型1. 定义模型2. 使用模型① 查询记录② 新增记录③ 删除记录④ 更新记录3. 字段设置4. 模型获取器5. 模型修改器6. 模型查询范围7. 模型数据集8. 模型的自动时间戳9. 模型的只读字…

为什么VMware会给我多创建了两个网络呢?Windows和Linux为什么可以彼此ping的通呢

为什么VMware会给我多创建了两个网络呢&#xff1f;Windows和Linux为什么可以彼此ping的通呢 文章目录为什么VMware会给我多创建了两个网络呢&#xff1f;Windows和Linux为什么可以彼此ping的通呢桥接模式ANT模式&#xff08;VMnet8&#xff09;仅主机模式&#xff08;VMnet1&a…

学习系统编程No.6【进程控制】

引言&#xff1a; 北京时间&#xff1a;2023/3/19/15:16&#xff0c;刚刚睡醒&#xff0c;我发现我真的能睡&#xff0c;早上将反向迭代器剩下的一些知识学完&#xff0c;发现&#xff0c;昨天那篇博客发的有些匆忙了&#xff0c;最后有关反向迭代器的知识都没有把精华部分给分…

使用旧电脑玩Linux

今天给大家讲讲使用旧电脑玩Linux&#xff0c;大家应该都知道旧电脑的硬件一般比较落后&#xff0c;特别是一些非常老的电脑&#xff0c;目前还在使用的是机械硬盘&#xff0c;如是要跑windows可想而知&#xff0c;但是Linux系统对硬件性能的要求可比windows低的多了&#xff0…

常用React Hooks大合集(二)

React Hooks useRef useRef返回一个ref对象&#xff0c;返回的ref对象再组件的整个生命周期保持不变。 最常用的ref是两种用法&#xff1a; 用法一&#xff1a;引入DOM&#xff08;或者组件&#xff0c;但是需要是class组件&#xff09;元素&#xff1b;用法二&#xff1a;保…

C#中的DataGridView中添加按钮并操作数据

背景&#xff1a;最近在项目中有需求需要在DataGridView中添加“删除”、“修改”按钮&#xff0c;用来对数据的操作以及显示。 在DataGridView中显示需要的按钮 首先在DataGridView中添加需要的列&#xff0c;此列是用来存放按钮的。 然后在代码中“画”按钮。 if (e.Column…

JVM监控搭建

文章目录JVM监控搭建整体架构JolokiaTelegrafInfluxdbGrafanaJVM监控搭建 整体架构 JVM 的各种内存信息&#xff0c;会通过 JMX 接口进行暴露。 Jolokia 组件负责把 JMX 信息翻译成容易读取的 HTTP 请求。Telegraf 组件作为一个通用的监控 agent&#xff0c;和 JVM 进程部署在…