文章目录
- 1.基本介绍
- 1.引出Seata
- 2.问题分析
- 2.Seata的安装和配置
- 1.解压到d盘
- 2.修改D:\seata\conf\file.conf文件
- 1.修改事务组
- 2.修改日志存储模式为db
- 3.修改数据库(MySQL5.7)连接信息
- 4.创建seata数据库
- 5.复制db_store.sql的内容,创建需要的表
- 6.修改registry.conf 配置注册中心nacos
- 3.启动测试
- 1.启动nacos
- 2.双击seata-server.bat,启动seata-server
- 3.输入http://192.168.137.1:8848/nacos/index.html查看注册情况
- 4.这个seata也是一个微服务,注册到Nacos
- 3.Seata分布式事务应用实例
- 1.工作流程分析
- 2.数据库表设计
- 1.订单微服务的数据库
- 2.库存微服务的数据库
- 3.账号微服务的数据库
- 4.检查数据库信息
- 5.分别为三个库创建对应的回滚日志表
- 1.脚本在seata的conf目录下 db_undo_log.sql
- 2.复制脚本为三个数据库创建回滚日志表
- 3.完整脚本
- 6.检查数据库信息
- 3.微服务模块 seata_storage_micro_service-10010
- netstat -aon | findstr :<端口号> 可以查看端口是否被占用
- 1.新建子模块
- 2.检查父子pom.xml
- 3.pom.xml引入依赖
- 4.application.yml
- 5.D:\seata\conf\file.conf 配置事务组和seata服务的ip+端口和db(前面都配置了)![image-20240331112135077](https://img-blog.csdnimg.cn/img_convert/9406fa41f2321367d0e94d12bfcd3c49.png)
- 6.再将这个文件复制到src/main/resources下,然后修改这行,seata的服务ip+端口以及db根据实际情况修改
- 7.复制D:\seata\conf\registry.conf到src/main/resources下,然后配置注册中心nacos(前面也配过,直接复制即可)
- 8.最终的文件目录
- 9.com/sun/springcloud/entity/Storage.java 创建实体类
- 10.com/sun/springcloud/dao/StorageDao.java 创建dao层
- 11.mapper/StorageMapper.xml 实现接口的方法
- 12.com/sun/springcloud/service/StorageService.java 编写service层接口
- 13.com/sun/springcloud/service/Impl/StorageServiceImpl.java 编写service层实现类
- 14.com/sun/springcloud/controller/StorageController.java 编写controller
- 15.com/sun/springcloud/config/MyBatisConfig.java 配置类,依赖注入所有Mapper接口(不用加@Mapper注解了)
- 16.com/sun/springcloud/config/DataSourceProxyConfig.java 配置数据源代理为 seata
- 17.com/sun/springcloud/SeataStorageMicroServiceApplication10010.java 创建主启动类
- 18.测试
- 1.启动nacos和seata
- 2.运行主启动类,并从nacos查看注册情况(如果有报错,先全部重启一下)
- 3.测试controller
- 4.微服务模块 seata_account_micro_service-10012
- 1.新建子模块
- 2.检查父子pom.xml
- 3.pom.xml 引入依赖
- 4.application.yml
- 5.拷贝10010的两个配置文件
- 6.com/sun/springcloud/entity/Account.java 创建实体类
- 7.com/sun/springcloud/dao/AccountDao.java 创建Mapper接口
- 8.mapper/AccountMapper.xml 创建Mapper.xml
- 9.com/sun/springcloud/service/AccountService.java 编写service的接口
- 10.com/sun/springcloud/service/Impl/AccountServiceImpl.java 编写service实现类
- 11.com/sun/springcloud/controller/AccountController.java 编写controller层
- 12.将10010的两个配置文件粘贴过来
- 13.编写主启动类
- 14.测试
- 1.启动nacos和seata
- 2.运行主启动类,并从nacos查看注册情况(如果有报错,先全部重启一下)
- 3.测试controller
- 5.微服务模块 seata-order-micro-service-10008
- 1.新建子模块
- 2.检查父子pom.xml
- 3.pom.xml引入依赖
- 4.application.yml
- 5.将两个配置文件粘贴过来
- 6.com/sun/springcloud/entity/Order.java 创建实体类
- 7.com/sun/springcloud/dao/OrderDao.java 编写Mapper接口
- 8.mapper/OrderMapper.xml 编写Mapper.xml
- 9.service层思路分析
- 10.com/sun/springcloud/service/OrderService.java 编写service接口
- 11.com/sun/springcloud/service/AccountService.java 声明要远程调用AccountController.java的接口
- 12.com/sun/springcloud/service/StorageService.java 声明要远程调用StorageController.java的接口
- 13.com/sun/springcloud/service/Impl/OrderServiceImpl.java 编写service实现类
- 14.com/sun/springcloud/controller/OrderController.java 编写controller
- 15.将两个配置类复制过来
- 16.编写主启动类
- 17.测试
- 1.启动nacos和seata
- 2.运行主启动类,并从nacos查看注册情况(如果有报错,先全部重启一下)
- 3.启动报错
- 4.解决问题
- 5.再次运行,查看nacos注册情况
1.基本介绍
1.引出Seata
2.问题分析
2.Seata的安装和配置
1.解压到d盘
2.修改D:\seata\conf\file.conf文件
1.修改事务组
2.修改日志存储模式为db
3.修改数据库(MySQL5.7)连接信息
4.创建seata数据库
# 创建数据库 seata
create database seata;
use seata;
5.复制db_store.sql的内容,创建需要的表
# 创建表
-- the table to store GlobalSession data
drop table if exists `global_table`;
create table `global_table` (`xid` varchar(128) not null,`transaction_id` bigint,`status` tinyint not null,`application_id` varchar(32),`transaction_service_group` varchar(32),`transaction_name` varchar(128),`timeout` int,`begin_time` bigint,`application_data` varchar(2000),`gmt_create` datetime,`gmt_modified` datetime,primary key (`xid`),key `idx_gmt_modified_status` (`gmt_modified`, `status`),key `idx_transaction_id` (`transaction_id`)
);-- the table to store BranchSession data
drop table if exists `branch_table`;
create table `branch_table` (`branch_id` bigint not null,`xid` varchar(128) not null,`transaction_id` bigint ,`resource_group_id` varchar(32),`resource_id` varchar(256) ,`lock_key` varchar(128) ,`branch_type` varchar(8) ,`status` tinyint,`client_id` varchar(64),`application_data` varchar(2000),`gmt_create` datetime,`gmt_modified` datetime,primary key (`branch_id`),key `idx_xid` (`xid`)
);-- the table to store lock data
drop table if exists `lock_table`;
create table `lock_table` (`row_key` varchar(128) not null,`xid` varchar(96),`transaction_id` long ,`branch_id` long,`resource_id` varchar(256) ,`table_name` varchar(32) ,`pk` varchar(36) ,`gmt_create` datetime ,`gmt_modified` datetime,primary key(`row_key`)
);
6.修改registry.conf 配置注册中心nacos
3.启动测试
1.启动nacos
2.双击seata-server.bat,启动seata-server
3.输入http://192.168.137.1:8848/nacos/index.html查看注册情况
4.这个seata也是一个微服务,注册到Nacos
3.Seata分布式事务应用实例
1.工作流程分析
2.数据库表设计
1.订单微服务的数据库
-- 订单微服务的数据库
CREATE DATABASE order_micro_service;
USE order_micro_service;
# 查看目前正在使用的数据库
select database();
# 创建订单表
CREATE TABLE `order`(id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,user_id BIGINT DEFAULT NULL ,product_id BIGINT DEFAULT NULL ,nums INT DEFAULT NULL ,money INT DEFAULT NULL,`status` INT DEFAULT NULL COMMENT '0:创建中; 1:已完结' );
select * from `order`;
2.库存微服务的数据库
-- 库存微服务的数据库
CREATE DATABASE storage_micro_service;
USE storage_micro_service;
CREATE TABLE `storage`(id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,product_id BIGINT DEFAULT NULL ,amount INT DEFAULT NULL COMMENT '库存量' );
-- 初始化库存表
INSERT INTO `storage` VALUES(NULL, 1, 10);
SELECT * FROM `storage`;
3.账号微服务的数据库
-- 账号微服务的数据库
CREATE DATABASE account_micro_service;
USE account_micro_service;
CREATE TABLE `account`(id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY ,user_id BIGINT DEFAULT NULL ,money INT DEFAULT NULL COMMENT '账户金额' );
-- 初始化账户表
INSERT INTO `account` VALUES(NULL, 666, 10000);
select * from `account`;
4.检查数据库信息
5.分别为三个库创建对应的回滚日志表
1.脚本在seata的conf目录下 db_undo_log.sql
2.复制脚本为三个数据库创建回滚日志表
CREATE TABLE `undo_log` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`branch_id` bigint(20) NOT NULL,`xid` varchar(100) NOT NULL,`context` varchar(128) NOT NULL,`rollback_info` longblob NOT NULL,`log_status` int(11) NOT NULL,`log_created` datetime NOT NULL,`log_modified` datetime NOT NULL,`ext` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
3.完整脚本
use order_micro_service;
CREATE TABLE `undo_log`
(`id` bigint(20) NOT NULL AUTO_INCREMENT,`branch_id` bigint(20) NOT NULL,`xid` varchar(100) NOT NULL,`context` varchar(128) NOT NULL,`rollback_info` longblob NOT NULL,`log_status` int(11) NOT NULL,`log_created` datetime NOT NULL,`log_modified` datetime NOT NULL,`ext` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDBAUTO_INCREMENT = 1DEFAULT CHARSET = utf8;use storage_micro_service;
CREATE TABLE `undo_log`
(`id` bigint(20) NOT NULL AUTO_INCREMENT,`branch_id` bigint(20) NOT NULL,`xid` varchar(100) NOT NULL,`context` varchar(128) NOT NULL,`rollback_info` longblob NOT NULL,`log_status` int(11) NOT NULL,`log_created` datetime NOT NULL,`log_modified` datetime NOT NULL,`ext` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDBAUTO_INCREMENT = 1DEFAULT CHARSET = utf8;use account_micro_service;
CREATE TABLE `undo_log`
(`id` bigint(20) NOT NULL AUTO_INCREMENT,`branch_id` bigint(20) NOT NULL,`xid` varchar(100) NOT NULL,`context` varchar(128) NOT NULL,`rollback_info` longblob NOT NULL,`log_status` int(11) NOT NULL,`log_created` datetime NOT NULL,`log_modified` datetime NOT NULL,`ext` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDBAUTO_INCREMENT = 1DEFAULT CHARSET = utf8
6.检查数据库信息
3.微服务模块 seata_storage_micro_service-10010
netstat -aon | findstr :<端口号> 可以查看端口是否被占用
1.新建子模块
2.检查父子pom.xml
3.pom.xml引入依赖
- 注意这里引入的nacos是nacos-discovery starter,没有整合Seata之前引入的跟这个不一样!!!
<dependencies><!-- 提示 application.yml --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><!-- 引入 openfeign --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!-- 在微服务模块引入 nacos-discovery starter --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--seata--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId><exclusions><!-- 排除自带的 seata-all --><exclusion><artifactId>seata-all</artifactId><groupId>io.seata</groupId></exclusion></exclusions></dependency><!-- 引入指定版本的 io.seata --><dependency><groupId>io.seata</groupId><artifactId>seata-all</artifactId><version>0.9.0</version></dependency><!-- springboot web starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><!-- 如果在子工程/模块指定了 version,则以指定为准 --></dependency><!--1. starter-actuator 是 springboot 程序的监控系统,可以实现健康检查,info 信息等2. 访问 http://localhost:10000/actuator 可以看到相关链接, 还可以做相关设置. --><!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><!-- mybatis --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency><!-- druid --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><!-- 这里我们重新指定一下 version 因为父项目中没有对这个依赖进行版本仲裁--><version>1.1.13</version></dependency><!-- mysql --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- jdbc --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- test --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- 公共模块的jar包 --><dependency><groupId>org.example</groupId><artifactId>e_commerce_center-common-api</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies>
4.application.yml
server:port: 10010 # 配置服务端口
spring:application:name: seata-storage-micor-service # 配置服务的名称,名字任意这里与项目名保持一致cloud:alibaba:seata: # 配置seatatx-service-group: sun_order_tx_group # 自定义事务组名称,与\conf\file.conf保持一致nacos: # 配置nacosdiscovery:server-addr: localhost:8848datasource: # 配置数据源driver-class-name: com.mysql.jdbc.Driver# 别忘记创建数据库之后修改数据库名称url: username: password:
logging: # 配置seata日志级别level:io:seata: info
mybatis:mapperLocations: classpath:mapper/*.xml # 扫描所有Mapper.xmlconfiguration:map-underscore-to-camel-case: true # 开启驼峰命名
5.D:\seata\conf\file.conf 配置事务组和seata服务的ip+端口和db(前面都配置了)
6.再将这个文件复制到src/main/resources下,然后修改这行,seata的服务ip+端口以及db根据实际情况修改
7.复制D:\seata\conf\registry.conf到src/main/resources下,然后配置注册中心nacos(前面也配过,直接复制即可)
8.最终的文件目录
9.com/sun/springcloud/entity/Storage.java 创建实体类
package com.sun.springcloud.entity;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** Description: 库存实体类** @Author sun* @Create 2024/3/31 11:41* @Version 1.0*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Storage {private Long id;private Long productId; // 这里使用的驼峰命名法,数据库中使用的是下划线命名法,可以自动映射private Integer amount;
}
10.com/sun/springcloud/dao/StorageDao.java 创建dao层
- 注意这里的@Param注解一旦指定,就不需要在xml实现的时候指定参数类型了,直接使用#{}来取出数据即可
package com.sun.springcloud.dao;import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;/*** Description:** @Author sun* @Create 2024/3/31 11:50* @Version 1.0*/
@Mapper
public interface StorageDao {//扣减库存信息,这里的@Param注解的作用是给参数命名,这样在xml文件中就可以直接使用该名称void reduce(@Param("productId") Long productId, @Param("nums") Integer nums);
}
11.mapper/StorageMapper.xml 实现接口的方法
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sun.springcloud.dao.StorageDao"><resultMap id="BaseResultMap" type="com.sun.springcloud.entity.Storage"><id column="id" property="id" jdbcType="BIGINT"/><result column="product_id" property="productId" jdbcType="BIGINT"/><result column="amount" property="amount" jdbcType="INTEGER"/></resultMap>
<!--实现void reduce(@Param("productId") Long productId, @Param("nums") Integer nums);--><update id="reduce">UPDATE storageSET amount = amount - #{nums}WHERE product_id = #{productId}</update></mapper>
12.com/sun/springcloud/service/StorageService.java 编写service层接口
package com.sun.springcloud.service;/*** Description:** @Author sun* @Create 2024/3/31 13:01* @Version 1.0*/
public interface StorageService {void reduce(Long productId, Integer nums);
}
13.com/sun/springcloud/service/Impl/StorageServiceImpl.java 编写service层实现类
package com.sun.springcloud.service.Impl;import com.sun.springcloud.dao.StorageDao;
import com.sun.springcloud.service.StorageService;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;import javax.annotation.Resource;/*** Description:** @Author sun* @Create 2024/3/31 13:02* @Version 1.0*/
@Service
@Slf4j
public class StorageServiceImpl implements StorageService {@Resourceprivate StorageDao storageDao;private static final Logger LOGGER =LoggerFactory.getLogger(StorageServiceImpl.class);@Overridepublic void reduce(Long productId, Integer nums) {LOGGER.info("==========seata_storage_micro_service-10010 扣 减 库 存 start==========");storageDao.reduce(productId, nums);LOGGER.info("==========seata_storage_micro_service-10010 扣 减 库 存 end==========");}
}
14.com/sun/springcloud/controller/StorageController.java 编写controller
package com.sun.springcloud.controller;import com.sun.springcloud.service.StorageService;
import com.sun.springcloud.util.Result;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/*** Description:** @Author sun* @Create 2024/3/31 13:07* @Version 1.0*/
@RestController
public class StorageController {@Resourceprivate StorageService storageService;//扣减库存@RequestMapping("/storage/reduce")public Result reduce(Long productId, Integer nums) {storageService.reduce(productId, nums);return Result.success("扣减库存成功 ok", null);}
}
15.com/sun/springcloud/config/MyBatisConfig.java 配置类,依赖注入所有Mapper接口(不用加@Mapper注解了)
package com.sun.springcloud.config;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;/*** Description: MyBatis配置类,扫描所有Mapper接口,这样就不用在每个Mapper接口上添加@Mapper注解** @Author sun* @Create 2024/3/31 13:16* @Version 1.0*/
@MapperScan("com.sun.springcloud.dao")
@Configuration
public class MyBatisConfig {
}
16.com/sun/springcloud/config/DataSourceProxyConfig.java 配置数据源代理为 seata
package com.sun.springcloud.config;import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import javax.sql.DataSource;/*** Description: 配置数据源的代理是 seata** @Author sun* @Create 2024/3/31 13:28* @Version 1.0*/
@Configuration
public class DataSourceProxyConfig {// 配置文件中的mybatis.mapperLocations@Value("${mybatis.mapperLocations}")private String mapperLocations; // mybatis的mapper文件位置// 配置数据源@Bean@ConfigurationProperties(prefix = "spring.datasource") // 读取配置文件中的数据源配置public DataSource druidDataSource() {return new DruidDataSource();}// 配置数据源代理为seata的DataSourceProxy@Bean // 配置数据源代理引入的包: io.seata.rm.datasource.DataSourceProxypublic DataSourceProxy dataSourceProxy(DataSource dataSource) {return new DataSourceProxy(dataSource);}// 配置SqlSessionFactory为seata的SqlSessionFactoryBean@Bean // 配置SqlSessionFactory,常规配置public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy)throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean =new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSourceProxy);sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations)); // mybatis的mapper文件位置sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());return sqlSessionFactoryBean.getObject();}
}
17.com/sun/springcloud/SeataStorageMicroServiceApplication10010.java 创建主启动类
package com.sun.springcloud;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;/*** Description:** @Author sun* @Create 2024/3/31 13:37* @Version 1.0*/
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) // 取消数据源的自动创建,使用代理数据源
@EnableDiscoveryClient // 开启服务发现
@EnableFeignClients // 开启Feign客户端
public class SeataStorageMicroServiceApplication10010 {public static void main(String[] args) {SpringApplication.run(SeataStorageMicroServiceApplication10010.class, args);}
}
18.测试
1.启动nacos和seata
2.运行主启动类,并从nacos查看注册情况(如果有报错,先全部重启一下)
3.测试controller
4.微服务模块 seata_account_micro_service-10012
1.新建子模块
2.检查父子pom.xml
3.pom.xml 引入依赖
<dependencies><!-- 提示 application.yml --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><!-- 引入 openfeign --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!-- 在微服务模块引入 nacos-discovery starter --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--seata--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId><exclusions><!-- 排除自带的 seata-all --><exclusion><artifactId>seata-all</artifactId><groupId>io.seata</groupId></exclusion></exclusions></dependency><!-- 引入指定版本的 io.seata --><dependency><groupId>io.seata</groupId><artifactId>seata-all</artifactId><version>0.9.0</version></dependency><!-- springboot web starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><!-- 如果在子工程/模块指定了 version,则以指定为准 --></dependency><!--1. starter-actuator 是 springboot 程序的监控系统,可以实现健康检查,info 信息等2. 访问 http://localhost:10000/actuator 可以看到相关链接, 还可以做相关设置. --><!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><!-- mybatis --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency><!-- druid --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><!-- 这里我们重新指定一下 version 因为父项目中没有对这个依赖进行版本仲裁--><version>1.1.13</version></dependency><!-- mysql --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- jdbc --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- test --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- 公共模块的jar包 --><dependency><groupId>org.example</groupId><artifactId>e_commerce_center-common-api</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies>
4.application.yml
server:port: 10012 # 配置服务端口
spring:application:name: seata-account-micor-service # 配置服务的名称,名字任意这里与项目名保持一致cloud:alibaba:seata: # 配置seatatx-service-group: sun_order_tx_group # 自定义事务组名称,与\conf\file.conf保持一致nacos: # 配置nacosdiscovery:server-addr: localhost:8848datasource: # 配置数据源driver-class-name: com.mysql.jdbc.Driver# 别忘记创建数据库之后修改数据库名称url: username: password:
logging: # 配置seata日志级别level:io:seata: info
mybatis:mapperLocations: classpath:mapper/*.xml # 扫描所有Mapper.xmlconfiguration:map-underscore-to-camel-case: true # 开启驼峰命名
5.拷贝10010的两个配置文件
6.com/sun/springcloud/entity/Account.java 创建实体类
package com.sun.springcloud.entity;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** Description:** @Author sun* @Create 2024/3/31 14:46* @Version 1.0*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {private Long id;private Long userId;private Integer money;
}
7.com/sun/springcloud/dao/AccountDao.java 创建Mapper接口
package com.sun.springcloud.dao;import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;/*** Description:** @Author sun* @Create 2024/3/31 14:48* @Version 1.0*/
@Mapper
public interface AccountDao {void reduce(@Param("userId") Long userId, @Param("money") Integer money);
}
8.mapper/AccountMapper.xml 创建Mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sun.springcloud.dao.AccountDao"><resultMap id="BaseResultMap" type="com.sun.springcloud.entity.Account"><id column="id" property="id" jdbcType="BIGINT"/><result column="user_id" property="userId" jdbcType="BIGINT"/><result column="money" property="money" jdbcType="INTEGER"/></resultMap><!-- 扣减金额 --><update id="reduce">UPDATE accountSETmoney = money - #{money}WHEREuser_id = #{userId};</update>
</mapper>
9.com/sun/springcloud/service/AccountService.java 编写service的接口
package com.sun.springcloud.service;/*** Description:** @Author sun* @Create 2024/3/31 14:59* @Version 1.0*/public interface AccountService {void reduce(Long userId, Integer money);
}
10.com/sun/springcloud/service/Impl/AccountServiceImpl.java 编写service实现类
package com.sun.springcloud.service.Impl;/*** Description:** @Author sun* @Create 2024/3/31 15:01* @Version 1.0*/import com.sun.springcloud.dao.AccountDao;
import com.sun.springcloud.service.AccountService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
@Service
public class AccountServiceImpl implements AccountService {private static final Logger LOGGER =LoggerFactory.getLogger(AccountServiceImpl.class);@ResourceAccountDao accountDao;@Overridepublic void reduce(Long userId, Integer money) {LOGGER.info("========seata_account_micro_service-10012 扣减账户余额 start ======");accountDao.reduce(userId,money);LOGGER.info("========seata_account_micro_service-10012 扣减账户余额 end ======");}
}
11.com/sun/springcloud/controller/AccountController.java 编写controller层
package com.sun.springcloud.controller;/*** Description:** @Author sun* @Create 2024/3/31 15:07* @Version 1.0*/import com.sun.springcloud.service.AccountService;
import com.sun.springcloud.util.Result;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@RestController
public class AccountController {@ResourceAccountService accountService;/*** 扣减账户余额*/@RequestMapping("/account/reduce")public Result result(@RequestParam("userId") Long userId, @RequestParam("money") Integer money){accountService.reduce(userId,money);return Result.success("200", "扣减账户余额 OK");}
}
12.将10010的两个配置文件粘贴过来
13.编写主启动类
package com.sun.springcloud;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;/*** Description:** @Author sun* @Create 2024/3/31 15:17* @Version 1.0*/
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) // 取消数据源的自动配置,使用seata代理数据源配置
@EnableFeignClients // 开启Feign
@EnableDiscoveryClient // 开启服务发现
public class SeataAccountMicroServiceApplication10012 {public static void main(String[] args) {SpringApplication.run(SeataAccountMicroServiceApplication10012.class, args);}}
14.测试
1.启动nacos和seata
2.运行主启动类,并从nacos查看注册情况(如果有报错,先全部重启一下)
3.测试controller
5.微服务模块 seata-order-micro-service-10008
1.新建子模块
2.检查父子pom.xml
3.pom.xml引入依赖
<dependencies><!-- 提示 application.yml --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><!-- 引入 openfeign --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!-- 在微服务模块引入 nacos-discovery starter --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--seata--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId><exclusions><!-- 排除自带的 seata-all --><exclusion><artifactId>seata-all</artifactId><groupId>io.seata</groupId></exclusion></exclusions></dependency><!-- 引入指定版本的 io.seata --><dependency><groupId>io.seata</groupId><artifactId>seata-all</artifactId><version>0.9.0</version></dependency><!-- springboot web starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><!-- 如果在子工程/模块指定了 version,则以指定为准 --></dependency><!--1. starter-actuator 是 springboot 程序的监控系统,可以实现健康检查,info 信息等2. 访问 http://localhost:10000/actuator 可以看到相关链接, 还可以做相关设置. --><!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><!-- mybatis --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency><!-- druid --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><!-- 这里我们重新指定一下 version 因为父项目中没有对这个依赖进行版本仲裁--><version>1.1.13</version></dependency><!-- mysql --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- jdbc --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- test --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- 公共模块的jar包 --><dependency><groupId>org.example</groupId><artifactId>e_commerce_center-common-api</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies>
4.application.yml
server:port: 10008 # 配置服务端口
spring:application:name: seata-order-micor-service # 配置服务的名称,名字任意这里与项目名保持一致cloud:alibaba:seata: # 配置seatatx-service-group: sun_order_tx_group # 自定义事务组名称,与\conf\file.conf保持一致nacos: # 配置nacosdiscovery:server-addr: localhost:8848datasource: # 配置数据源driver-class-name: com.mysql.jdbc.Driver# 别忘记创建数据库之后修改数据库名称url: username: password:
logging: # 配置seata日志级别level:io:seata: info
mybatis:mapperLocations: classpath:mapper/*.xml # 扫描所有Mapper.xmlconfiguration:map-underscore-to-camel-case: true # 开启驼峰命名
5.将两个配置文件粘贴过来
6.com/sun/springcloud/entity/Order.java 创建实体类
package com.sun.springcloud.entity;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** Description:** @Author sun* @Create 2024/3/31 15:48* @Version 1.0*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {private Long id;private Long userId;private Long productId;private Integer nums;private Integer money;private Integer status;
}
7.com/sun/springcloud/dao/OrderDao.java 编写Mapper接口
package com.sun.springcloud.dao;/*** Description:** @Author sun* @Create 2024/3/31 15:51* @Version 1.0*/import com.sun.springcloud.entity.Order;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;@Mapper
public interface OrderDao {//新建订单void save(Order order);//修改订单状态void update(@Param("userId") Long userId, @Param("status") Integer status);
}
8.mapper/OrderMapper.xml 编写Mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sun.springcloud.dao.OrderDao"><resultMap id="BaseResultMap" type="com.sun.springcloud.entity.Order"><id column="id" property="id" jdbcType="BIGINT"/><result column="user_id" property="userId" jdbcType="BIGINT"/><result column="product_id" property="productId" jdbcType="BIGINT"/><result column="nums" property="nums" jdbcType="INTEGER"/><result column="money" property="money" jdbcType="INTEGER"/><result column="status" property="status" jdbcType="INTEGER"/></resultMap><insert id="save">insert into `order` (id, user_id, product_id, nums, money, status)values (null, #{userId}, #{productId}, #{nums}, #{money}, 0);</insert><update id="update">update `order`set status = 1where user_id = #{userId}and status = #{status};</update>
</mapper>
9.service层思路分析
10.com/sun/springcloud/service/OrderService.java 编写service接口
package com.sun.springcloud.service;import com.sun.springcloud.entity.Order;/*** Description:** @Author sun* @Create 2024/3/31 16:09* @Version 1.0*/
public interface OrderService {void save(Order order);
}
11.com/sun/springcloud/service/AccountService.java 声明要远程调用AccountController.java的接口
package com.sun.springcloud.service;import com.sun.springcloud.util.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;/*** Description:** @Author sun* @Create 2024/3/31 16:13* @Version 1.0*/
@FeignClient(value = "seata-account-micor-service")
public interface AccountService {@RequestMapping("/account/reduce")public Result result(@RequestParam("userId") Long userId, @RequestParam("money") Integer money);
}
12.com/sun/springcloud/service/StorageService.java 声明要远程调用StorageController.java的接口
package com.sun.springcloud.service;import com.sun.springcloud.util.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;/*** Description:** @Author sun* @Create 2024/3/31 16:19* @Version 1.0*/
@FeignClient(value = "seata-storage-micor-service")
public interface StorageService {@RequestMapping("/storage/reduce")public Result reduce(Long productId, Integer nums);
}
13.com/sun/springcloud/service/Impl/OrderServiceImpl.java 编写service实现类
package com.sun.springcloud.service.Impl;import com.sun.springcloud.dao.OrderDao;
import com.sun.springcloud.entity.Order;
import com.sun.springcloud.service.AccountService;
import com.sun.springcloud.service.OrderService;
import com.sun.springcloud.service.StorageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;import javax.annotation.Resource;/*** Description:** @Author sun* @Create 2024/3/31 16:48* @Version 1.0*/
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {@Resource // 注入OrderDao对象private OrderDao orderDao;@Resource // 注入针对接口的代理对象,这里是AccountService接口的代理对象,进行远程调用private AccountService accountService;@Resource // 注入针对接口的代理对象,这里是StorageService接口的代理对象,进行远程调用private StorageService storageService;@Overridepublic void save(Order order) {log.info("----->下单开始");log.info("----->本地生成订单开始");// 1.保存订单orderDao.save(order);log.info("----->本地生成订单结束");log.info("----->订单微服务开始调用账户,扣减账户余额开始");// 2.调用远程服务,扣减账户余额accountService.result(order.getUserId(), order.getMoney());log.info("----->订单微服务开始调用账户,扣减账户余额结束");log.info("----->订单微服务开始调用库存,扣减库存开始");// 3.调用远程服务,扣减库存storageService.reduce(order.getProductId(), order.getNums());log.info("----->订单微服务开始调用库存,扣减库存结束");log.info("----->修改订单状态开始");// 4.修改订单状态orderDao.update(order.getUserId(), 0);log.info("----->修改订单状态结束");log.info("----->下单结束");}
}
14.com/sun/springcloud/controller/OrderController.java 编写controller
package com.sun.springcloud.controller;import com.sun.springcloud.entity.Order;
import com.sun.springcloud.service.OrderService;
import com.sun.springcloud.util.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/*** Description:** @Author sun* @Create 2024/3/31 17:06* @Version 1.0*/
@RestController
public class OrderController {@Resource // 依赖注入针对接口的代理对象,这里是OrderService接口的代理对象private OrderService orderService;@GetMapping("/order/save")public Result save(Order order) { // 这里没有使用@RequestBody注解,前端可以直接使用url传参orderService.save(order);return Result.success("订单创建成功", null);}
}
15.将两个配置类复制过来
16.编写主启动类
package com.sun.springcloud;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;/*** Description:** @Author sun* @Create 2024/3/31 19:09* @Version 1.0*/
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) // 排除数据源的自动配置
@EnableDiscoveryClient
@EnableFeignClients
public class SeataOrderMicroServiceApplication10008 {public static void main(String[] args) {SpringApplication.run(SeataOrderMicroServiceApplication10008.class, args);}
}
17.测试
1.启动nacos和seata
2.运行主启动类,并从nacos查看注册情况(如果有报错,先全部重启一下)
3.启动报错
- 这个显示是方法的参数有问题
4.解决问题
被远程调用的微服务模块的参数使用@RequestParam
远程调用微服务模块时也需要加上@RequestParam
- 使用GET方式时,通常需要加
@RequestParam
:这是因为GET请求主要用于从服务器检索特定资源。在GET请求中,请求参数通常通过URL的查询字符串部分传递,而@RequestParam
正是用于处理这类参数的。它告诉Spring框架如何从请求的查询参数映射值到方法的参数上。 - 使用POST方式时,通常需要加
@RequestBody
:POST请求通常用于向服务器提交数据,这些数据作为请求的主体发送。@RequestBody
注解允许你的方法接收一个来自客户端的JSON(或其他格式)请求体,并将其反序列化为Java对象。这意味着客户端可以发送复杂的数据结构作为请求体的一部分,而服务端可以直接操作这些数据结构的Java表示。 - 总结就是在微服务中,如果使用controller使用get方式,则需要在参数中添加@RequestParam,如果controller中使用post方式,就需要在参数中添加@RequestBody