Spring Boot集成atomikos快速入门Demo

1.什么是atomikos

Atomikos是一个轻量级的分布式事务管理器,实现了Java Transaction API (JTA)规范,可以很方便的和Spring Boot集成,支持微服务场景下跨节点的全局事务。Atomikos公司官方网址为:https://www.atomikos.com/。其旗下最著名的产品就是事务管理器。产品分两个版本:

  • TransactionEssentials:开源的免费产品

  • ExtremeTransactions:上商业版,需要收费。

2.环境搭建

第一个mysql数据库

docker run --name docker-mysql -e MYSQL_ROOT_PASSWORD=123456 -p 3333:3306 -d mysql

第二个mysql数据库

docker run --name docker-mysql-2 -e MYSQL_ROOT_PASSWORD=123456 -p 3334:3306 -d mysql

初始化数据

create database demo;
create table user_info
(
user_id     varchar(64)          not null primary key,
username    varchar(100)         null ,
age         int(3)               null ,
gender      tinyint(1)           null ,
remark      varchar(255)         null ,
create_time datetime             null ,
create_id   varchar(64)          null ,
update_time datetime             null ,
update_id   varchar(64)          null ,
enabled     tinyint(1) default 1 null
);

说明

msyql账号root
mysql密码123456

3.项目代码

实验目的:实现2个mysql数据的分布式事务管理,要么全部成功,只要有一个失败就会滚。

pom..xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>springboot-demo</artifactId><groupId>com.et</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>atomikos</artifactId><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.0</version><!-- 1.3.0以上的版本没有@MapperScan以及@Select注解 --></dependency><!-- automatic+jta的分布式事务管理 --><!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jta-atomikos --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jta-atomikos</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><!--boot 2.1默认 mysql8的版本; boot 2.0默认mysql5版本--><version>8.0.13</version><!--<version>5.1.46</version>--><!--<scope>runtime</scope>--></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional><version>1.18.2</version></dependency></dependencies>
</project>

mapper

创建2个mapper连接不同的数据库

package com.et.atomikos.mapper1;import org.apache.catalina.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;public interface UserInfoMapper1 {// query@Select("SELECT * FROM user_info WHERE username = #{username}")User findByName(@Param("username") String username);// add@Insert("INSERT INTO user_info(user_id,username, age) VALUES(#{userId},#{username}, #{age})")int insert(@Param("userId") String userId,@Param("username") String username, @Param("age") Integer age);
}
package com.et.atomikos.mapper2;import org.apache.catalina.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;public interface UserInfoMapper2 {// query@Select("SELECT * FROM user_info WHERE username = #{username}")User findByName(@Param("username") String username);// add@Insert("INSERT INTO user_info(user_id,username, age) VALUES(#{userId},#{username}, #{age})")int insert(@Param("userId") String userId,@Param("username") String username, @Param("age") Integer age);
}

service

创建2个service,分别用不同mapper

package com.et.atomikos.mapper1;import com.et.atomikos.mapper1.UserInfoMapper1;
import com.et.atomikos.mapper2.UserInfoMapper2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
public class ManyService1 {@Autowiredprivate UserInfoMapper1 userInfoMapper1;@Autowiredprivate UserInfoMapper2 userInfoMapper2;@Transactionalpublic int insert(String userId,String username, Integer age) {int insert = userInfoMapper1.insert(userId,username, age);int i = 1 / age;// if age is zero  ,then a error will be happened.return insert;}@Transactionalpublic int insertDb1AndDb2(String userId,String username, Integer age) {int insert = userInfoMapper1.insert(userId,username, age);int insert2 = userInfoMapper2.insert(userId,username, age);int i = 1 / age;// if age is zero  ,then a error will be happened.return insert + insert2;}}
package com.et.atomikos.mapper2;import com.et.atomikos.mapper2.UserInfoMapper2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
public class ManyService2 {@Autowiredprivate UserInfoMapper2 userInfoMapper2;@Transactionalpublic int insert(String userId,String username, Integer age) {int i = userInfoMapper2.insert(userId,username, age);System.out.println("userInfoMapper2.insert end :" + null);int a = 1 / 0;//touch a errorreturn i;}}

config

初始化数据源1和数据源2

package com.et.atomikos.config;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;@Data
@ConfigurationProperties(prefix = "spring.datasource.test1")
public class DBConfig1 {// @Value("${mysql.datasource.test1.jdbcurl}")//@Value("${jdbcurl}")private String jdbcurl;//private String url;private String username;private String password;private int minPoolSize;private int maxPoolSize;private int maxLifetime;private int borrowConnectionTimeout;private int loginTimeout;private int maintenanceInterval;private int maxIdleTime;private String testQuery;
}
package com.et.atomikos.config;import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.mysql.cj.jdbc.MysqlXADataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.sql.DataSource;
import java.sql.SQLException;/*** @author liuhaihua* @version 1.0* @ClassName MyBatisConfig1* @Description todo* @date 2024年04月18日 13:37*/@Configuration
@MapperScan(basePackages = "com.et.atomikos.mapper1", sqlSessionTemplateRef = "test1SqlSessionTemplate")
public class MyBatisConfig1 {@Bean(name = "test1DataSource")  //test1DataSourcepublic DataSource testDataSource(DBConfig1 testConfig) throws SQLException {MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();//mysqlXaDataSource.setUrl(testConfig.getUrl());mysqlXaDataSource.setUrl(testConfig.getJdbcurl());mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);mysqlXaDataSource.setPassword(testConfig.getPassword());mysqlXaDataSource.setUser(testConfig.getUsername());mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);// 将本地事务注册到创 Atomikos全局事务AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();xaDataSource.setXaDataSource(mysqlXaDataSource);xaDataSource.setUniqueResourceName("test1DataSource");xaDataSource.setMinPoolSize(testConfig.getMinPoolSize());xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize());xaDataSource.setMaxLifetime(testConfig.getMaxLifetime());xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout());xaDataSource.setLoginTimeout(testConfig.getLoginTimeout());xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval());xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime());xaDataSource.setTestQuery(testConfig.getTestQuery());return xaDataSource;}@Bean(name = "test1SqlSessionFactory")public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource)throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(dataSource);return bean.getObject();}@Bean(name = "test1SqlSessionTemplate")public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {return new SqlSessionTemplate(sqlSessionFactory);}
}

数据源2也是类似配置

controller

package com.et.atomikos.controller;import com.et.atomikos.mapper1.ManyService1;
import com.et.atomikos.mapper2.ManyService2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@RestController
public class HelloWorldController {@Autowiredprivate ManyService1 manyService1;@Resourceprivate ManyService2 manyService2;//http://localhost:8088/datasource1?userId=9&username=datasource1&age=2@RequestMapping(value = "datasource1")public int datasource1(String userId,String username, Integer age) {return manyService1.insert(userId,username, age);}//http://localhost:8088/datasource2?userId=9&username=datasource2&age=2@RequestMapping(value = "datasource2")public int datasource2(String userId,String username, Integer age) {return manyService2.insert(userId,username, age);}//http://localhost:8088/insertDb1AndDb2?userId=1&username=tom5&age=2//http://localhost:8088/insertDb1AndDb2?userId=2&username=tom5&age=0  //touch a error@RequestMapping(value = "insertDb1AndDb2")public int insertDb1AndDb2(String userId,String username, Integer age) {return manyService1.insertDb1AndDb2(userId,username, age);}}

application.yaml

server:port: 8088spring:application:name: manyDatasourcedatasource:#  spring.datasource.test1#    druid:test1:jdbcurl: jdbc:mysql://localhost:3333/demo?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8username: rootpassword: 123456initial-size: 1min-idle: 1max-active: 20test-on-borrow: truedriver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourceminPoolSize: 3maxPoolSize: 25maxLifetime: 20000borrowConnectionTimeout: 30loginTimeout: 30maintenanceInterval: 60maxIdleTime: 60test2:jdbcurl: jdbc:mysql://localhost:3334/demo?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8username: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourceminPoolSize: 3maxPoolSize: 25maxLifetime: 20000borrowConnectionTimeout: 30loginTimeout: 30maintenanceInterval: 60maxIdleTime: 60mybatis:mapper-locations: classpath:mapper/*.xmlspring.resources.static-locations: classpath:static/,file:static/logging:level:czs: debugorg.springframework: WARNorg.spring.springboot.dao: debug

以上只是一些关键代码,所有代码请参见下面代码仓库

代码仓库

  • https://github.com/Harries/springboot-demo

4.测试

启动Spring Boot 应用

插入第一个数据测试

http://localhost:8088/datasource1?userId=9&username=datasource1&age=2

插入第二个数据库

http://localhost:8088/datasource2?userId=9&username=datasource2&age=2

同时插入2个数据库

http://localhost:8088/insertDb1AndDb2?userId=1&username=tom5&age=2

异常回滚测试

http://localhost:8088/insertDb1AndDb2?userId=2&username=tom5&age=0  //touch a error

5.参考

  • https://github.com/ColoZhu/springbootmanyDatasource

  • http://www.liuhaihua.cn/archives/710435.html

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)

文章目录 一、JavaFx介绍1、JavaFx简介2、可用性3、主要特征4、UI控件 二、JavaFx概述1、JavaFx结构图2、JavaFx组件&#xff08;1&#xff09;舞台&#xff08;2&#xff09;场景① 场景图② 节点 &#xff08;3&#xff09;控件&#xff08;4&#xff09;布局&#xff08;5&a…

ubuntu在docker容器中安装strongswan

1.起动一个ubuntu容器&#xff0c;我是用的docker compose启动的&#xff0c;compose的配置文件为ipsec-strongswan.yml services:ipsec-strongswan:image: ubuntu:22.04container_name: ipsec-strongswancap_add:- NET_ADMIN- SYS_ADMIN- SYS_MODULEcommand: "tail -f /…

Unity射击游戏开发教程:(2)实例化和销毁游戏对象

现在我们有了“飞船”,我们可以在屏幕上移动它,现在我们需要发射一些激光!与宇宙飞船一样,我们将让事情变得简单并使用 Unity 自己的基本形状。舱体的效果很好,所以我们来创建一个。 我们保存了有关位置、旋转和缩放的信息。我们想要缩小这个对象,假设每个轴上缩小到 0.2…

【声呐仿真】学习记录1-配置dave、uuv_simulator

【声呐仿真】学习记录1-配置dave、uuv_simulator 1.介绍2.配置3.一些场景 1.介绍 家|DAVE项目 — Home | Project DAVE 2.配置 参考官方教程安装|DAVE项目 — Installation | Project DAVE mkdir -p ~/uuv_ws/src cd ~/uuv_ws/src git clone https://github.com/Field-Robot…

二维图像的双线性插值

1. 原理 见下图,假设原图为单通道的灰度图,想求图像中某点Q(x,y)的灰度值。 2. 代码实现 #include <iostream> #include <stdio.h> #include <stdint.h> #include <string> #include<opencv2/opencv.hpp> #include<opencv2/core.hpp>…

踏上R语言之旅:解锁数据世界的神秘密码(二)

R语言学习 文章目录 R语言学习1.数据的R语言表示2.多元数据的R语言调用3.多元数据的简单R语言分析 总结 1.数据的R语言表示 数据框&#xff08;data frame) R语言中用函数data.frame()生成数据框&#xff0c;其句法是&#xff1a; data.frame(data1,data2,…)&#xff0c;例如…

OpenHarmony网络协议通信—kcp

kcp 是一种 ARQ 协议,可解决在网络拥堵情况下 tcp 协议的网络速度慢的问题 下载安装 直接在 OpenHarmony-SIG 仓中搜索 kcp 并下载。 使用说明 准备一套完整的 OpenHarmony 3.1 Beta 代码 库代码存放路径&#xff1a;./third_party/kcp 修改添加依赖的编译脚本 在/develo…

Bridge 2024(Br2024):革新文件管理,提升创意思维的卓越工具

Bridge 2024&#xff08;Br2024&#xff09;是一款专为Mac和Windows用户设计的数字媒体管理软件&#xff0c;以其出色的文件管理和项目管理功能&#xff0c;成为创意专业人士不可或缺的工作伙伴。这款软件不仅继承了Adobe Bridge一贯的直观界面和强大功能&#xff0c;更在多个方…

基于 LLM 大模型 Agent 的实践总结和困境分享

节前&#xff0c;我们组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学&#xff0c;针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 基于大模…

树莓派+Openwrt连接校园网,打破校园网设备限制

前言 因为本校学生校园网只允许最多三个设备登录&#xff0c;对于同时拥有多个联网设备的我十分不友好&#xff0c;而且大多单片机如esp32的wifi模块是只允许一般的WPA/WPA2认证的&#xff0c;是不支持校园网的portal认证。所以我决定搞一个路由器。 然后我上网买了一个TP-Li…

【ThinkPHP框架教程·Part-01】ThinkPHP6.x框架安装教程

文章目录 一、框架介绍1、框架简介和版本选择2、主要新特性 二、安装步骤1、下载并运行Composer-Setup.exe2、安装TP前切换镜像3、安装稳定版4、测试运行 一、框架介绍 1、框架简介和版本选择 Thinkphp是一种基于php的开源web应用程序开发框架ThinkPHP框架&#xff0c;是免费开…

基于注解配置bean

文章目录 1.基本使用1.基本介绍2.快速入门1.引入jar包2.MyComponent.java3.UserAction.java3.UserDao.java4.UserService.java5.beans05.xml6.断点查看bean对象是否创建7.测试 3.注意事项和细节 2.自己实现spring注解1.需求分析2.思路分析图3.编写自定义注解ComponentScan4.编写…

【AI面试】工作和面试过程中,经常遇到的其他问题汇总(持续更新)

在与面试官在面对面进行交流的过程中,面试官不仅仅会针对简历中记录的内容进行深入的了解,还会进行一些发散性的提问。 目前也就很明显,就是要看看对方: 对于常见的问题是如何思考的?有没有在持续性的学习本领域的新知识?对于不知道的问题,是如何思考的?迁移能力咋样。…

Mybaties入门

文章目录 介绍持久层框架Jdbc缺点Mybaties简化整体框架ORM方式 Mybaties快速入门(Mapper代理)核心配置文件映射文件配置编写会话工具类 介绍 Mybaties是一款优秀的持久层框架&#xff0c;用于简化JDBC开发 持久层 负责将数据保存到数据库的那一层代码JavaEE三层架构&#x…

【双曲几何】圆盘上的三角形概念

目录 一、说明二、对偶三角形概念2.1 反演关系2.2 对偶关系2.3 找出三角形的对偶三角形 三、正交三角形概念3.1 通过对偶三角形&#xff0c;找到垂心3.2 正交三角形的概念3.3 中心射影点的概念 四、后记 一、说明 本文对双曲空间的三角形进行分析&#xff0c;本篇首先给出&am…

数据结构(data structure)(2)链表的运用

桶排序 e*len/(max1) e为每个元素&#xff0c;根据上式判断该元素放入哪个桶 桶排序适用于分布均匀的数组1.arr->length,max 2.Node[]-new Node[length] 3.扫描->hash->下标->元素入桶 4.出桶<>排序排序的输出private void sort(int[] arr){int lengtharr.le…

漆包线行业你了解多少?专业漆包线行业MES生产管理系统

今天就说说漆包线行业&#xff0c;漆包线是工业电机&#xff08;包括电动机和发电机&#xff09;、变压器、电工仪表、电力及电子元器件、电动工具、家用电器、汽车电器等用来绕制电磁线圈的主要材料。 漆包线上游是铜杆行业&#xff0c;下游是各种消费终端&#xff0c;主要是电…

Java基础知识总结(66)

** FileOutputStream练习 */ public class FileOutputStreamDemo { public static void main(String[] args) { String path "D:\\IoDemo\\test2.txt"; //如果文件不存在&#xff0c;则自动创建 //append:是指是否在原有内容后追加&#xff0c;默认为FALSE try(Outp…

Python代码实战——深入解析缓存问题:穿透、击穿、雪崩

作为Python开发者处理缓存相关问题,可以通过具体的场景和代码示例来更好地理解和解决缓存穿透、缓存击穿和缓存雪崩。 缓存穿透 场景:用户频繁请求数据库中不存在的数据,导致每次请求都绕过缓存直接查询数据库,增加数据库的压力。 解决方案: 设置空值缓存:当查询数据不…

代码优化实践之税率计算问题

开篇 今天的问题来自于《编程珠玑》第三章【数据决定程序结构】&#xff0c;这里提出了几条代码优化相关的原则&#xff0c;受益不浅。下面是提到的几条原则&#xff1a; 使用数组重新编写重复代码。冗长的相似代码往往可以使用最简单的数据结构——数组来更好的表述&#xff1…