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

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
云数据库 RDS PostgreSQL,高可用系列 2核4GB
简介: Spring Boot中如何实现分布式事务

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

今天,我们来探讨一下在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实现分布式事务。分布式事务保证了在多个数据库操作之间的数据一致性,是分布式系统中不可或缺的重要机制。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
23天前
|
存储 安全 Java
管理 Spring 微服务中的分布式会话
在微服务架构中,管理分布式会话是确保用户体验一致性和系统可扩展性的关键挑战。本文探讨了在 Spring 框架下实现分布式会话管理的多种方法,包括集中式会话存储和客户端会话存储(如 Cookie),并分析了它们的优缺点。同时,文章还涵盖了与分布式会话相关的安全考虑,如数据加密、令牌验证、安全 Cookie 政策以及服务间身份验证。此外,文中强调了分布式会话在提升系统可扩展性、增强可用性、实现数据一致性及优化资源利用方面的显著优势。通过合理选择会话管理策略,结合 Spring 提供的强大工具,开发人员可以在保证系统鲁棒性的同时,提供无缝的用户体验。
|
2月前
|
监控 Java API
Spring Boot 3.2 结合 Spring Cloud 微服务架构实操指南 现代分布式应用系统构建实战教程
Spring Boot 3.2 + Spring Cloud 2023.0 微服务架构实践摘要 本文基于Spring Boot 3.2.5和Spring Cloud 2023.0.1最新稳定版本,演示现代微服务架构的构建过程。主要内容包括: 技术栈选择:采用Spring Cloud Netflix Eureka 4.1.0作为服务注册中心,Resilience4j 2.1.0替代Hystrix实现熔断机制,配合OpenFeign和Gateway等组件。 核心实操步骤: 搭建Eureka注册中心服务 构建商品
375 3
|
23天前
|
NoSQL Java 调度
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
分布式锁是分布式系统中用于同步多节点访问共享资源的机制,防止并发操作带来的冲突。本文介绍了基于Spring Boot和Redis实现分布式锁的技术方案,涵盖锁的获取与释放、Redis配置、服务调度及多实例运行等内容,通过Docker Compose搭建环境,验证了锁的有效性与互斥特性。
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
|
5月前
|
人工智能 负载均衡 Java
Spring AI Alibaba 发布企业级 MCP 分布式部署方案
本文介绍了Spring AI Alibaba MCP的开发与应用,旨在解决企业级AI Agent在分布式环境下的部署和动态更新问题。通过集成Nacos,Spring AI Alibaba实现了流量负载均衡及节点变更动态感知等功能。开发者可方便地将企业内部业务系统发布为MCP服务或开发自己的AI Agent。文章详细描述了如何通过代理应用接入存量业务系统,以及全新MCP服务的开发流程,并提供了完整的配置示例和源码链接。未来,Spring AI Alibaba计划结合Nacos3的mcp-registry与mcp-router能力,进一步优化Agent开发体验。
1991 15
|
5月前
|
监控 Java 调度
SpringBoot中@Scheduled和Quartz的区别是什么?分布式定时任务框架选型实战
本文对比分析了SpringBoot中的`@Scheduled`与Quartz定时任务框架。`@Scheduled`轻量易用,适合单机简单场景,但存在多实例重复执行、无持久化等缺陷;Quartz功能强大,支持分布式调度、任务持久化、动态调整和失败重试,适用于复杂企业级需求。文章通过特性对比、代码示例及常见问题解答,帮助开发者理解两者差异,合理选择方案。记住口诀:单机简单用注解,多节点上Quartz;若是任务要可靠,持久化配置不能少。
494 4
|
8月前
|
NoSQL Java Redis
Springboot使用Redis实现分布式锁
通过这些步骤和示例,您可以系统地了解如何在Spring Boot中使用Redis实现分布式锁,并在实际项目中应用。希望这些内容对您的学习和工作有所帮助。
706 83
|
7月前
|
存储 Java 文件存储
🗄️Spring Boot 3 整合 MinIO 实现分布式文件存储
本文介绍了如何基于Spring Boot 3和MinIO实现分布式文件存储。随着应用规模扩大,传统的单机文件存储方案难以应对大规模数据和高并发访问,分布式文件存储系统成为更好的选择。文章详细讲解了MinIO的安装、配置及与Spring Boot的整合步骤,包括Docker部署、MinIO控制台操作、Spring Boot项目中的依赖引入、配置类编写及工具类封装等内容。最后通过一个上传头像的接口示例展示了具体的开发和测试过程,强调了将API操作封装成通用工具类以提高代码复用性和可维护性的重要性。
1302 7
🗄️Spring Boot 3 整合 MinIO 实现分布式文件存储
|
11月前
|
存储 Java 关系型数据库
在Spring Boot中整合Seata框架实现分布式事务
可以在 Spring Boot 中成功整合 Seata 框架,实现分布式事务的管理和处理。在实际应用中,还需要根据具体的业务需求和技术架构进行进一步的优化和调整。同时,要注意处理各种可能出现的问题,以保障分布式事务的顺利执行。
758 53
|
10月前
|
存储 NoSQL Java
使用lock4j-redis-template-spring-boot-starter实现redis分布式锁
通过使用 `lock4j-redis-template-spring-boot-starter`,我们可以轻松实现 Redis 分布式锁,从而解决分布式系统中多个实例并发访问共享资源的问题。合理配置和使用分布式锁,可以有效提高系统的稳定性和数据的一致性。希望本文对你在实际项目中使用 Redis 分布式锁有所帮助。
577 5
|
10月前
|
缓存 NoSQL Java
Spring Boot中的分布式缓存方案
Spring Boot提供了简便的方式来集成和使用分布式缓存。通过Redis和Memcached等缓存方案,可以显著提升应用的性能和扩展性。合理配置和优化缓存策略,可以有效避免常见的缓存问题,保证系统的稳定性和高效运行。
274 3

热门文章

最新文章