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

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

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

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值