SpringBoot整合ShardingJdbc实现XA分布式事务

2023-11-15

SpringBoot整合ShardingJdbc实现XA分布式事务

什么是分布式事务?
关于分布式事务的介绍,请参考ShardingJdbc的介绍:分布式事务

官方文档分布式事务使用示例:使用示例,点进去之后,拉到最下面即可看到 官方example

我这里仅做一个最简单的演示:
首先说明一下数据库相关信息

  • 我这里使用了两个数据源,3306mysql服务和3307mysql服务,我本地起了两个mysql服务,端口号分别是3306和3307
  • 3306mysql服务创建了数据库test,3307mysql服务创建了数据库t
  • 数据库test创建了表test,数据库t创建了表t_name
  • 通过ShardingJdbc管理这两个数据源

关键依赖如下:

<properties>
        <java.version>1.8</java.version>
        <sharding-sphere.version>4.1.1</sharding-sphere.version>
</properties>
<dependencies>
        <!-- ShardingJdbc SpringBootStarter -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>${sharding-sphere.version}</version>
        </dependency>

        <!-- 使用XA事务时,需要引入此模块 -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-transaction-xa-core</artifactId>
            <version>${sharding-sphere.version}</version>
        </dependency>

        <!-- 使用BASE事务时,需要引入此模块 -->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>sharding-transaction-base-seata-at</artifactId>-->
<!--            <version>${sharding-sphere.version}</version>-->
<!--        </dependency>-->

 </dependencies>

然后是application.properties文件:

# ShardingJdbc 这里设置了两个数据源 3306的test库 3307的t库 33063307是我本地的两个mysql服务

# 显示sql
spring.shardingsphere.props.sql.show=true

# 配置真实数据源名称分别为:ds0 ds1
spring.shardingsphere.datasource.names=ds0,ds1

# 配置第 1 个数据源 ds0
spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&autoReconnect=true&failOverReadOnly=false
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=root

# 配置第 2 个数据源 ds1
spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://localhost:3307/t?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&autoReconnect=true&failOverReadOnly=false
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=root

# 未配置分片规则的表将通过默认数据源定位 没有配置下面 actual-data-nodes 的表都会去ds0执行相关操作
spring.shardingsphere.sharding.default-data-source-name=ds0

#数据库的表配置 说明:两个数据库不存在分库分表这些 就是单纯的两个毫无关系的数据库 其中 test表在3306(ds0)的test库   t_name表在3307(ds1)的t库
#更多说明请参考官方文档:https://shardingsphere.apache.org/document/legacy/4.x/document/cn/overview/;
#历史版本配置:https://github.com/apache/shardingsphere/blob/master/docs/document/content/others/api-change-history/shardingsphere-jdbc/spring-boot-starter.en.md
spring.shardingsphere.sharding.tables.test.actual-data-nodes=ds0.test
spring.shardingsphere.sharding.tables.t_name.actual-data-nodes=ds1.t_name

准备工作做好就该写代码了,代码很少,除了启动类,只有两个类,一个配置类,一个测试类。
首先是配置类,和官方文档一模一样:

@Configuration
@EnableTransactionManagement
public class TransactionConfiguration {

    @Bean
    public PlatformTransactionManager txManager(final DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean
    public JdbcTemplate jdbcTemplate(final DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

然后是启动类:

@SpringBootApplication
@Import(TransactionConfiguration.class)	//注意这里:导入上面的配置类
public class ShardingjdbctxApplication {
//省略无关代码...
}

最后是测试类:

	@Transactional(rollbackFor = Exception.class)
    @ShardingTransactionType(TransactionType.XA)
    @GetMapping("test")
    public void test() {
        TransactionType txType0 = jdbcTemplate.execute("INSERT INTO t_name (name) VALUES (?)", (PreparedStatementCallback<TransactionType>) preparedStatement -> {
            //t_name表只有主键id和name两个字段
            preparedStatement.setString(1, UUID.randomUUID().toString().replaceAll("-", ""));
            preparedStatement.executeUpdate();
            return TransactionTypeHolder.get();
        });

        System.out.println("txType0====>>>" + txType0);

        TransactionType txType1 = jdbcTemplate.execute("UPDATE test SET age = ? WHERE id = 1", (PreparedStatementCallback<TransactionType>) preparedStatement -> {
            //test表只有主键id和age两个字段
            preparedStatement.setInt(1, 18);
            preparedStatement.executeUpdate();
            return TransactionTypeHolder.get();
        });

        System.out.println("txType1====>>>" + txType1);

        //抛出异常 事务回滚
        int a = 10 / 0;
    }

如此,便会回滚事务。

此外,一些疑惑:

  • 不添加 @ShardingTransactionType(TransactionType.XA) 注解,或者该注解设置成 TransactionType.LOCAL 也会回滚事务
  • rollbackFor 设置成 NullPointerException 也会回滚事务,但是上面抛出的分明是 ArithmeticException: / by zero

具体原因,暂时不知,待进一步了解其原理再来说明。也希望路过的大神不吝赐教~

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SpringBoot整合ShardingJdbc实现XA分布式事务 的相关文章

  • 如何使用 SimpleDateFormat 解析多种格式的日期

    我正在尝试解析文档中的一些日期 用户似乎以类似但不完全相同的格式输入了这些日期 以下是格式 9 09 9 2009 09 2009 9 1 2009 9 1 2009 尝试解析所有这些内容的最佳方法是什么 这些似乎是最常见的 但我想让我困扰
  • Spring安全“记住我”cookie在第一个请求中不可用

    我无法在登录请求后检索 Spring 记住我 cookie 但它在对受保护页面的下一个请求中工作正常 谁能告诉我怎样才能立即得到它 我在登录请求中设置了记住我的 cookie 但在 Spring 重定向回原始 受保护的 url 后无法检索它
  • 来自数据库的 jfreechart 散点图

    如何使用java中的jfreechart绘制mysql数据库表中数据的散点图 我使用过 Swing 库 任何链接都会有帮助 我搜索了谷歌但找不到理解的解决方案 如果您有代码 请提供给我 实际上我确实做了条形图并使用 jfreechart 绘
  • Java:使用 HttpURLConnection 的 HTTP PUT

    如何执行 HTTP PUT 我正在使用的类似乎认为它正在执行 PUT 但端点将其视为我执行了 GET 我做错了什么吗 URL url new URL https HttpURLConnection conn HttpURLConnectio
  • eclipse中导入项目文件夹图标

    我在 Eclipse 工作区中新导入的 Maven 项目有J and M项目文件夹顶部的图标 项目和包资源管理器 而其他导入的 Maven 项目只有一个J icon 有人可以解释其中的区别吗 该项目有J装饰器被称为 Java 项目和具有M装
  • 条件触发器的Django迁移sql

    我想创建一个触发器 仅在满足条件时插入表 我尝试过使用 IF BEGIN END 和 WHERE 的各种组合 但 Django 每次都会返回 SQL 语法错误 这里 type user id指的是触发该事件的人 user id指的是接收到通
  • 在文本文件中搜索单词并返回其频率

    如何在包含单词文本的文本文件中搜索特定单词并返回其频率或出现次数 使用扫描仪 String text Question how to search for a particular word in a text file containin
  • Condition 接口中的 signalAll 与对象中的 notificationAll

    1 昨天我才问过这个问题条件与等待通知机制 https stackoverflow com questions 10395571 condition vs wait notify mechanism 2 我想编辑相同的内容并在我的问题中添加
  • 主线程如何在该线程之前运行?

    我有以下代码 public class Derived implements Runnable private int num public synchronized void setA int num try Thread sleep 1
  • 如何从 Retrofit2 获取字符串响应?

    我正在做 android 正在寻找一种方法来执行超级基本的 http GET POST 请求 我不断收到错误 java lang IllegalArgumentException Unable to create converter for
  • 隐式超级构造函数 Person() 未定义。必须显式调用另一个构造函数?

    我正在开发一个项目 但收到错误 隐式超级构造函数 Person 未定义 必须显式调用另一个构造函数 我不太明白它 这是我的人物课程 public class Person public Person String name double D
  • 如何配置 WebService 返回 ArrayList 而不是 Array?

    我有一个在 jax ws 上实现的 java Web 服务 此 Web 服务返回用户的通用列表 它运行得很好 Stateless name AdminToolSessionEJB RemoteBinding jndiBinding Admi
  • Espresso 和 Proguard 的 Java.lang.NoClassDefFoundError

    我对 Espresso 不太有经验 但我终于成功地运行了它 我有一个应用程序需要通过 Proguard 缩小才能处于 56K 方法之下 该应用程序以 3 秒的动画开始 因此我需要等到该动画结束才能继续 这就是我尝试用该方法做的事情waitF
  • 尝试使用等于“是”或“否”的字符串变量重新启动 do-while 循环

    计算行程距离的非常简单的程序 一周前刚刚开始 我有这个循环用于解决真或假问题 但我希望它适用于简单的 是 或 否 我为此分配的字符串是答案 public class Main public static void main String a
  • 解析输入,除了 System.in.read() 之外不使用任何东西

    我很难找到具体的细节System in read 有效 也许有人可以帮助我 似乎扫描仪会更好 但我不允许使用它 我被分配了一个任务 我应该以 Boolean Operator Boolean 的形式读取控制台用户输入 例如T F 或 T T
  • 为什么java中的for-each循环中需要声明变量

    for 每个循环的通常形式是这样的 for Foo bar bars bar doThings 但如果我想保留 bar 直到循环结束 我可以not使用 foreach 循环 Foo bar null Syntax error on toke
  • 无法捕获 Spring Batch 的 ItemWriter 中的异常

    我正在编写一个 Spring Batch 流程来将数据集从一个系统迁移到另一个系统 在这种情况下 这就像使用RowMapper实现在传递给查询之前从查询构建对象ItemWriter The ItemWriter称为save我的 DAO 上的
  • 解决错误javax.mail.AuthenticationFailedException

    我不熟悉java中发送邮件的这个功能 我在发送电子邮件重置密码时遇到错误 希望你能给我一个解决方案 下面是我的代码 public synchronized static boolean sendMailAdvance String emai
  • 如何在Java中正确删除数组[重复]

    这个问题在这里已经有答案了 我刚接触 Java 4 天 从我搜索过的教程来看 讲师们花费了大量精力来解释如何分配二维数组 例如 如下所示 Foo fooArray new Foo 2 3 但我还没有找到任何解释如何删除它们的信息 从内存的情
  • Java &= 运算符应用 & 或 && 吗?

    Assuming boolean a false 我想知道是否这样做 a b 相当于 a a b logical AND a is false hence b is not evaluated 或者另一方面 这意味着 a a b Bitwi

随机推荐