Spring Boot中如何实现分布式事务

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用版 2核4GB 50GB
简介: Spring Boot中如何实现分布式事务

Spring Boot中如何实现分布式事务

大家好,我是微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天,我们来探讨一下在Spring Boot中如何实现分布式事务。

一、什么是分布式事务

分布式事务是指在分布式系统中涉及多个数据库或服务的事务操作。它确保这些操作要么全部成功,要么全部失败,从而保持数据的一致性。实现分布式事务的常见方法包括两阶段提交(2PC)、补偿事务模式(TCC)、和消息队列事务。

二、使用Spring Cloud和Atomikos实现分布式事务

在Spring Boot中,我们可以使用Spring Cloud和Atomikos来实现分布式事务。Atomikos是一个Java事务处理器,支持XA和非XA事务。

1. 引入依赖

pom.xml中添加以下依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.atomikos</groupId>
        <artifactId>transactions-jta</artifactId>
        <version>5.0.8</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jta-atomikos</artifactId>
    </dependency>
    <!-- 其他依赖 -->
</dependencies>

2. 配置数据源

application.yml中配置多个数据源:

spring:
  jta:
    enabled: true

  datasource:
    primary:
      xa:
        data-source-class-name: com.mysql.cj.jdbc.MysqlXADataSource
      url: jdbc:mysql://localhost:3306/primary_db
      username: root
      password: password
    secondary:
      xa:
        data-source-class-name: com.mysql.cj.jdbc.MysqlXADataSource
      url: jdbc:mysql://localhost:3306/secondary_db
      username: root
      password: password

3. 配置Atomikos数据源和事务管理器

创建配置类来配置Atomikos数据源和JPA事务管理器:

package cn.juwatech.config;

import com.atomikos.jdbc.AtomikosDataSourceBean;
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBeanWrapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class DataSourceConfig {
   

    @Bean(name = "primaryDataSource")
    public DataSource primaryDataSource() {
   
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setUniqueResourceName("primary");
        xaDataSource.setXaDataSourceClassName("com.mysql.cj.jdbc.MysqlXADataSource");
        xaDataSource.setXaProperties(primaryXaProperties());
        return new AtomikosDataSourceBeanWrapper(xaDataSource);
    }

    @Bean(name = "secondaryDataSource")
    public DataSource secondaryDataSource() {
   
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setUniqueResourceName("secondary");
        xaDataSource.setXaDataSourceClassName("com.mysql.cj.jdbc.MysqlXADataSource");
        xaDataSource.setXaProperties(secondaryXaProperties());
        return new AtomikosDataSourceBeanWrapper(xaDataSource);
    }

    @Bean(name = "primaryEntityManager")
    public LocalContainerEntityManagerFactoryBean primaryEntityManager() {
   
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(primaryDataSource());
        em.setPackagesToScan("cn.juwatech.primary");
        em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        em.setJpaPropertyMap(jpaProperties());
        return em;
    }

    @Bean(name = "secondaryEntityManager")
    public LocalContainerEntityManagerFactoryBean secondaryEntityManager() {
   
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(secondaryDataSource());
        em.setPackagesToScan("cn.juwatech.secondary");
        em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        em.setJpaPropertyMap(jpaProperties());
        return em;
    }

    @Bean(name = "transactionManager")
    public JpaTransactionManager transactionManager() {
   
        JpaTransactionManager tm = new JpaTransactionManager();
        tm.setEntityManagerFactory(primaryEntityManager().getObject());
        tm.setDataSource(primaryDataSource());
        return tm;
    }

    private Map<String, String> primaryXaProperties() {
   
        Map<String, String> xaProperties = new HashMap<>();
        xaProperties.put("databaseName", "primary_db");
        xaProperties.put("user", "root");
        xaProperties.put("password", "password");
        return xaProperties;
    }

    private Map<String, String> secondaryXaProperties() {
   
        Map<String, String> xaProperties = new HashMap<>();
        xaProperties.put("databaseName", "secondary_db");
        xaProperties.put("user", "root");
        xaProperties.put("password", "password");
        return xaProperties;
    }

    private Map<String, Object> jpaProperties() {
   
        Map<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto", "update");
        properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
        return properties;
    }
}

4. 创建实体类和仓库接口

定义两个实体类和对应的仓库接口:

package cn.juwatech.primary;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class PrimaryEntity {
   
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    // getters and setters
}
package cn.juwatech.secondary;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class SecondaryEntity {
   
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String description;

    // getters and setters
}
package cn.juwatech.primary;

import org.springframework.data.jpa.repository.JpaRepository;

public interface PrimaryRepository extends JpaRepository<PrimaryEntity, Long> {
   
}
package cn.juwatech.secondary;

import org.springframework.data.jpa.repository.JpaRepository;

public interface SecondaryRepository extends JpaRepository<SecondaryEntity, Long> {
   
}

5. 创建服务层

创建一个服务类,包含跨多个数据库的事务操作:

package cn.juwatech.service;

import cn.juwatech.primary.PrimaryEntity;
import cn.juwatech.primary.PrimaryRepository;
import cn.juwatech.secondary.SecondaryEntity;
import cn.juwatech.secondary.SecondaryRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class TransactionalService {
   

    private final PrimaryRepository primaryRepository;
    private final SecondaryRepository secondaryRepository;

    public TransactionalService(PrimaryRepository primaryRepository, SecondaryRepository secondaryRepository) {
   
        this.primaryRepository = primaryRepository;
        this.secondaryRepository = secondaryRepository;
    }

    @Transactional
    public void executeDistributedTransaction() {
   
        PrimaryEntity primaryEntity = new PrimaryEntity();
        primaryEntity.setName("Primary Name");
        primaryRepository.save(primaryEntity);

        SecondaryEntity secondaryEntity = new SecondaryEntity();
        secondaryEntity.setDescription("Secondary Description");
        secondaryRepository.save(secondaryEntity);

        // 模拟异常以验证事务回滚
        if (true) {
   
            throw new RuntimeException("Simulated exception to trigger rollback");
        }
    }
}

6. 创建控制器

在控制器中调用服务层的方法,触发分布式事务:

package cn.juwatech.controller;

import cn.juwatech.service.TransactionalService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TransactionalController {
   

    private final TransactionalService transactionalService;

    public TransactionalController(TransactionalService transactionalService) {
   
        this.transactionalService = transactionalService;
    }

    @GetMapping("/transaction")
    public String executeTransaction() {
   
        try {
   
            transactionalService.executeDistributedTransaction();
            return "Transaction executed successfully";
        } catch (Exception e) {
   
            return "Transaction failed: " + e.getMessage();
        }
    }
}

通过本文,我们了解了如何在Spring Boot项目中使用Atomikos和Spring Cloud实现分布式事务。分布式事务保证了在多个数据库操作之间的数据一致性,是分布式系统中不可或缺的重要机制。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
2天前
|
存储 Java 文件存储
Spring Boot中的分布式文件系统
Spring Boot中的分布式文件系统
|
2天前
|
缓存 NoSQL Java
Spring Boot中的分布式缓存方案
Spring Boot中的分布式缓存方案
|
3天前
|
缓存 NoSQL Java
Spring Boot中的分布式缓存方案
Spring Boot中的分布式缓存方案
|
3天前
|
存储 Java 数据库
Spring Boot与分布式事务的最佳实践
Spring Boot与分布式事务的最佳实践
|
3天前
|
缓存 Java 机器人
Spring Boot中如何集成Hazelcast实现分布式缓存
Spring Boot中如何集成Hazelcast实现分布式缓存
|
负载均衡 Java Spring
SpringBoot学习笔记-14:第十四章-SpringBoot 与分布式(2)
SpringBoot学习笔记-14:第十四章-SpringBoot 与分布式
109 0
|
Dubbo Java 应用服务中间件
SpringBoot学习笔记-14:第十四章-SpringBoot 与分布式(1)
SpringBoot学习笔记-14:第十四章-SpringBoot 与分布式
114 0
|
负载均衡 Dubbo Java
SpringBoot学习笔记-14:第十四章-SpringBoot 与分布式
SpringBoot学习笔记-14:第十四章-SpringBoot 与分布式
108 0
|
3天前
|
JavaScript Java 测试技术
基于SpringBoot+Vue的大学生心理健康诊断专家系统的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue的大学生心理健康诊断专家系统的详细设计和实现(源码+lw+部署文档+讲解等)
13 0
|
3天前
|
JavaScript Java 测试技术
基于SpringBoot+Vue的大学生成绩管理系统的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue的大学生成绩管理系统的详细设计和实现(源码+lw+部署文档+讲解等)
9 0

热门文章

最新文章