锋哥原创的分布式事务框架Seata视频教程:
实战阿里分布式事务框架Seata视频教程(无废话,通俗易懂版)_哔哩哔哩_bilibili实战阿里分布式事务框架Seata视频教程(无废话,通俗易懂版)共计10条视频,包括:1 阿里分布式事务框架Seata简介、2 分布式事务简介、3 SpringCloud Alibaba分布式基础案例搭建等,UP主更多精彩视频,请关注UP账号。https://www.bilibili.com/video/BV1Uf4y1579F/我们模拟一个简单下单业务,客户端调用rest对外服务,rest服务再调用订单服务实现创建订单和账户服务实现账户扣钱操作,最终来完整下单业务;
2.1 案例架构设计
所有服务都注册到nacos中,方便feign远程调用;订单服务,账户服务各自有独立数据库;架构设计如下图:
整体项目结构如下图:
seatatest
是父项目,主要是做一些依赖管理,依赖版本管理,管理所有子module项目;
seata-common
子项目,主要是引入其他子项目需要的公共依赖,以及公共实体,工具类,配置类的统一封装;
seata-order
子项目,主要提供订单服务,生成订单;
seata-account
子项目,主要提供账户服务,根据订单扣钱操作;
seata-web
子项目,主要处理客户端下单请求,feign远程调用order,和account服务接口,最终完成下单处理;
2.2 数据库设计
我们新建两个数据库,分别是db_order
(订单数据库),db_account
(账户数据库),
db_order
数据库里面新建表t_order
订单表:
CREATE TABLE `t_order` (`id` int(11) NOT NULL AUTO_INCREMENT,`orderNo` varchar(100) DEFAULT NULL,`userId` int(11) DEFAULT NULL,`count` int(11) DEFAULT NULL,`amount` int(11) DEFAULT NULL,`remark` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8
db_account
数据库里面新建表t_account
用户账户表:
CREATE TABLE `t_account` (`id` int(11) NOT NULL AUTO_INCREMENT,`userId` int(11) DEFAULT NULL,`balance` int(11) DEFAULT NULL,`remark` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
插入数据:
insert into `t_account` (`id`, `userId`, `balance`, `remark`) values('1','1','2000','jack的账户');insert into `t_account` (`id`, `userId`, `balance`, `remark`) values('2','2','1000','marry的账户');
2.3 seatatest父项目搭建
seatatest是父项目,主要是做一些依赖管理,依赖版本管理,管理所有子module项目;
注意,它的packaging
类型是pom
pom.xml:
4.0.0com.java1234seatatestpom1.0-SNAPSHOTseata-commonseata-orderseata-accountseata-webUTF-81.81.81.1.10Hoxton.SR82.3.2.RELEASE2.2.4.RELEASE1.2.353.61.0-SNAPSHOT2.1.0org.springframework.cloudspring-cloud-dependencies${spring-cloud.version}pomimportorg.springframework.bootspring-boot-dependencies${springboot.version}pomimportcom.alibaba.cloudspring-cloud-alibaba-dependencies${springcloudalibaba.version}pomimportcom.alibabadruid${druid.version}com.alibabafastjson${fastjson.version}org.apache.commonscommons-lang3${commons-lang3.version}com.java1234seata-common${seata-common.version}org.mybatis.spring.bootmybatis-spring-boot-starter${mybatis.version}org.springframework.bootspring-boot-maven-plugin
2.4 seata-common子项目搭建
seata-common
子项目,主要是引入其他子项目需要的公共依赖,以及公共实体,工具类,配置类的统一封装;
项目结构:
seatatestcom.java12341.0-SNAPSHOT4.0.0seata-commonorg.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-starter-testtestcom.alibaba.cloudspring-cloud-starter-alibaba-nacos-discoverycom.alibaba.cloudspring-cloud-starter-alibaba-nacos-configorg.springframework.bootspring-boot-devtoolsruntimetruemysqlmysql-connector-javaruntimeorg.projectlomboklombokorg.springframework.bootspring-boot-starter-testtestorg.mybatis.spring.bootmybatis-spring-boot-startercom.alibabadruidorg.springframework.bootspring-boot-starter-data-redisorg.apache.commonscommons-pool2com.alibabafastjsoncommons-codeccommons-codecorg.apache.commonscommons-lang3
订单实体Order
:
package com.java1234.entity;/**订单表实体 * @author java1234_小锋 * @site www.java1234.com * @company 南通小锋网络科技有限公司 * @create 2021-07-13 10:26 */public class Order {private Integer id; // 编号private String orderNo; // 订单号private Integer userId; // 用户编号private Integer count; // 购买数量private Integer amount; // 购买金额private String remark; // 备注public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getOrderNo() {return orderNo;}public void setOrderNo(String orderNo) {this.orderNo = orderNo;}public Integer getUserId() {return userId;}public void setUserId(Integer userId) {this.userId = userId;}public Integer getCount() {return count;}public void setCount(Integer count) {this.count = count;}public Integer getAmount() {return amount;}public void setAmount(Integer amount) {this.amount = amount;}public String getRemark() {return remark;}public void setRemark(String remark) {this.remark = remark;}}
账户实体Account
:
package com.java1234.entity;/** * 用户账户表 * @author java1234_小锋 * @site www.java1234.com * @company 南通小锋网络科技有限公司 * @create 2021-07-13 10:36 */public class Account {private Integer id; // 编号private Integer userId; // 用户编号private Integer balance; // 账户余额private String remark; // 备注public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public Integer getUserId() {return userId;}public void setUserId(Integer userId) {this.userId = userId;}public Integer getBalance() {return balance;}public void setBalance(Integer balance) {this.balance = balance;}public String getRemark() {return remark;}public void setRemark(String remark) {this.remark = remark;}}
2.5 seata-order子项目搭建
seata-order
子项目,主要提供订单服务,生成订单;
项目结构:
pom.xml
:
seatatestcom.java12341.0-SNAPSHOT4.0.0seata-ordercom.java1234seata-common
application.yml
:
server:port: 8081servlet:context-path: /spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/db_order?serverTimezone=Asia/Shanghaiusername: rootpassword: 123456cloud:nacos:discovery:server-addr: 127.0.0.1:8848application:name: seata-ordermybatis:mapper-locations: classpath:mybatis/mapper/*.xml
启动类OrderApplication
:
package com.java1234;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication@MapperScan("com.java1234.mapper")@EnableDiscoveryClientpublic class OrderApplication {public static void main(String[] args) {SpringApplication.run(OrderApplication.class, args);}}
OrderMapper.xml
:
insert into t_order values(null,#{orderNo},#{userId},#{count},#{amount},#{remark})
OrderMapper
接口:
package com.java1234.mapper;import com.java1234.entity.Order;/** * @author java1234_小锋 * @site www.java1234.com * @company 南通小锋网络科技有限公司 * @create 2021-07-13 10:43 */public interface OrderMapper {/** * 创建订单 * @param order */void createOrder(Order order);}
OrderService
接口:
package com.java1234.service;import com.java1234.entity.Order;/** * 订单service接口 * @author java1234_小锋 * @site www.java1234.com * @company 南通小锋网络科技有限公司 * @create 2021-07-14 11:00 */public interface OrderService {/** * 创建订单 * @param order */void createOrder(Order order);}
OrderServiceImpl
实现类:
package com.java1234.service.impl;import com.java1234.entity.Order;import com.java1234.mapper.OrderMapper;import com.java1234.service.OrderService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;/** * @author java1234_小锋 * @site www.java1234.com * @company 南通小锋网络科技有限公司 * @create 2021-07-14 11:02 */@Service("orderService")public class OrderServiceImpl implements OrderService {@Autowiredprivate OrderMapper orderMapper;@Overridepublic void createOrder(Order order) {orderMapper.createOrder(order);}}
OrderController
:
package com.java1234.controller;import com.java1234.entity.Order;import com.java1234.service.OrderService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.UUID;/** * @author java1234_小锋 * @site www.java1234.com * @company 南通小锋网络科技有限公司 * @create 2021-07-14 11:13 */@RestController@RequestMapping("/seata")public class OrderController {@Autowiredprivate OrderService orderService;/** * 创建订单 * @param order * @return */@PostMapping("/createOrder")public boolean createOrder(@RequestBody Order order){System.out.println("order:"+order);order.setOrderNo(UUID.randomUUID().toString());// 生成订单IDorderService.createOrder(order);return true;}}
2.6 seata-account子项目搭建
seata-account
子项目,主要提供账户服务,根据订单扣钱操作;
项目结构:
pom.xml
:
seatatestcom.java12341.0-SNAPSHOT4.0.0seata-accountcom.java1234seata-common
application.yml
:
server:port: 8082servlet:context-path: /spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/db_account?serverTimezone=Asia/Shanghaiusername: rootpassword: 123456cloud:nacos:discovery:server-addr: 127.0.0.1:8848application:name: seata-accountmybatis:mapper-locations: classpath:mybatis/mapper/*.xml
AccountApplication
启动类:
package com.java1234;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication@MapperScan("com.java1234.mapper")@EnableDiscoveryClientpublic class AccountApplication {public static void main(String[] args) {SpringApplication.run(AccountApplication.class, args);}}
AccountMapper.xml
:
UPDATE t_account SET balance=balance-#{amount} WHERE userId=#{userId}
AccountMapper
接口:
package com.java1234.mapper;import java.util.Map;/** * 账户Mapper接口 * @author java1234_小锋 * @site www.java1234.com * @company 南通小锋网络科技有限公司 * @create 2021-07-13 10:43 */public interface AccountMapper {/** * 账户扣钱 */void decrease(Map map);}
AccountService
接口:
package com.java1234.service;import java.util.Map;/** * 账户service接口 * @author java1234_小锋 * @site www.java1234.com * @company 南通小锋网络科技有限公司 * @create 2021-07-15 12:58 */public interface AccountService {/** * 账户扣钱 */void decrease(Map map);}
`AccountServiceImpl`接口实现类:
package com.java1234.service.impl;import com.java1234.mapper.AccountMapper;import com.java1234.service.AccountService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.Map;/** * 账户Service实现类 * @author java1234_小锋 * @site www.java1234.com * @company 南通小锋网络科技有限公司 * @create 2021-07-15 13:00 */@Service("accountService")public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountMapper accountMapper;@Overridepublic void decrease(Map map) {accountMapper.decrease(map);}}
AccountController
:
package com.java1234.controller;import com.java1234.service.AccountService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;import java.util.Map;/** * 账户Controller * @author java1234_小锋 * @site www.java1234.com * @company 南通小锋网络科技有限公司 * @create 2021-07-15 13:06 */@RestController@RequestMapping("/account")public class AccountController {@Autowiredprivate AccountService accountService;/** * 给指定用户账户扣钱 * @param amount * @param userId * @return */@PostMapping("/decrease")public boolean decrease(@RequestParam("amount")Integer amount, @RequestParam("userId")Integer userId){System.out.println("amount:"+amount+",userId:"+userId);Map map=new HashMap();map.put("amount",amount);map.put("userId",userId);accountService.decrease(map);return true;}}
2.7 seata-web子项目搭建
seata-web
子项目,主要处理客户端下单请求,feign远程调用order,和account服务接口,最终完成下单处理;
项目结构:
pom.xml
:
seatatestcom.java12341.0-SNAPSHOT4.0.0seata-webcom.java1234seata-commonorg.springframework.cloudspring-cloud-starter-openfeign
application.yml
:
server:port: 80servlet:context-path: /spring:cloud:nacos:discovery:server-addr: 127.0.0.1:8848application:name: seata-web
WebApplication
:
package com.java1234;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;@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})@EnableFeignClients(basePackages = "com.java1234.feign")@EnableDiscoveryClientpublic class WebApplication {public static void main(String[] args) {SpringApplication.run(WebApplication.class, args);}}
OrderFeignService
:
package com.java1234.feign;import com.java1234.entity.Order;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;/** * 订单接口feign远程调用 * @author java1234_小锋 * @site www.java1234.com * @company 南通小锋网络科技有限公司 * @create 2021-07-16 10:55 */@FeignClient("seata-order")public interface OrderFeignService {/** * 创建订单 * @param order * @return */@PostMapping("/seata/createOrder")public boolean createOrder(@RequestBody Order order);}
AccountFeignService
:
package com.java1234.feign;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestParam;/** * 账号接口feign远程调用 * @author java1234_小锋 * @site www.java1234.com * @company 南通小锋网络科技有限公司 * @create 2021-07-16 10:55 */@FeignClient("seata-account")public interface AccountFeignService {/** * 账号扣钱 * @param amount * @param userId * @return */@PostMapping("/account/decrease")public boolean decrease(@RequestParam("amount")Integer amount, @RequestParam("userId")Integer userId);}
WebController
:
package com.java1234.controller;import com.java1234.entity.Order;import com.java1234.feign.AccountFeignService;import com.java1234.feign.OrderFeignService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RestController;/** * web-rest接口 * @author java1234_小锋 * @site www.java1234.com * @company 南通小锋网络科技有限公司 * @create 2021-07-15 16:32 */@RestControllerpublic class WebController {@Autowiredprivate OrderFeignService orderFeignService;@Autowiredprivate AccountFeignServiceaccountFeignService;/** * 下单 1,创建订单 2,账户扣钱 * @param order * @return */@PostMapping("/shopping")public boolean shopping(Order order){orderFeignService.createOrder(order); // 创建订单accountFeignService.decrease(order.getAmount(),order.getUserId()); // 账户扣钱return true;}}
2.8 postman测试
首先启动Nacos服务注册中心:
项目启动:
Nacos控制台:
服务注册成功!
测试接口:http://localhost/shopping
接口测试OK:
订单表生成订单:
账户表id=1的jack账户成功扣减180元;