初识Seata(二)
目录
1.背景
2.环境
3.业务模型
4.数据库准备
1)订单库
2)库存库
3)账户库
5.微服务准备
1)pom.xml
2)注册中心准备
3)订单服务
1.背景
书接上回:初识Seata(一),本文以一个Seata AT模式为例,介绍Seata的使用。
2.环境
SpringBoot | 2.1.2.RELEASE |
SpringCloud | Greenwich.SR1 |
spring-cloud-alibaba-dependencies | 2.1.0.RELEASE |
seata | 1.2.0 |
mysql | 5.1.47 |
3.业务模型
下订单 --> 扣减库存 --> 扣减账户余额 --> 修改订单状态
4.数据库准备
1)订单库
CREATE DATABASE IF NOT EXISTS `seata_order`
USE `seata_order`;CREATE DATABASE IF NOT EXISTS `orders` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`user_id` bigint(20) DEFAULT NULL,`product_id` bigint(20) DEFAULT NULL,`count` int(11) DEFAULT NULL COMMENT '数量',`money` decimal(10,2) DEFAULT NULL,`status` varchar(100) DEFAULT NULL,`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=55 DEFAULT CHARSET=utf8;CREATE TABLE IF NOT EXISTS `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`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2)库存库
CREATE DATABASE IF NOT EXISTS `seata_storage`
USE `seata_storage`;CREATE DATABASE IF NOT EXISTS `storage` (`id` bigint(11) NOT NULL AUTO_INCREMENT,`product_id` bigint(11) DEFAULT NULL COMMENT '产品id',`total` int(11) DEFAULT NULL COMMENT '总库存',`used` int(11) DEFAULT NULL COMMENT '已用库存',`residue` int(11) DEFAULT NULL COMMENT '剩余库存',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;CREATE TABLE IF NOT EXISTS `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`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3)账户库
CREATE DATABASE IF NOT EXISTS `seata_account`
USE `seata_account`;CREATE DATABASE IF NOT EXISTS `account` (`id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT 'id',`user_id` bigint(11) DEFAULT NULL COMMENT '用户id',`total` decimal(10,2) DEFAULT NULL COMMENT '总额度',`used` decimal(10,2) DEFAULT NULL COMMENT '已用额度',`balance` decimal(10,2) DEFAULT '0.00' COMMENT '剩余可用额度',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;CREATE TABLE IF NOT EXISTS `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`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
5.微服务准备
1)pom.xml
基础的包如下,不再赘述:spring-boot-dependencies/ spring-cloud-dependencies/ spring-cloud-alibaba-dependencies/ mysql-connector-java/ mybatis-spring-boot-starter/ druid/ druid-spring-boot-starter/ junit/ log4j/ lombok/ javax.annotation-api
关于seata的依赖包:
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-seata</artifactId><version>2.1.0.RELEASE</version><exclusions><exclusion><artifactId>seata-all</artifactId><groupId>io.seata</groupId></exclusion></exclusions>
</dependency>
<dependency><groupId>io.seata</groupId><artifactId>seata-all</artifactId><version>1.2.0</version>
</dependency>
2)注册中心准备
*** pom.xml就不再赘述了
- application.yml
server:port: 8801eureka:instance:hostname: localhostprefer-ip-address: trueclient:service-url:defaultZone: http://localhost:8801/eureka/registerWithEureka: falsefetchRegistry: false
- 启动类
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableEurekaServer
public class EurekaAppMain8801
{public static void main(String[] args){SpringApplication.run(EurekaAppMain8801.class, args);}
}
3)订单服务
目录结构
- java
- com.seata.order
- config
- controller
- dao
- entity
- service
- com.seata.order
- resources
- mapper(package)
- application.yml
- file.conf
- registry.conf
*** file.conf
transport {type = "TCP"server = "NIO"heartbeat = trueenableClientBatchSendRequest = truethreadFactory {bossThreadPrefix = "NettyBoss"workerThreadPrefix = "NettyServerNIOWorker"serverExecutorThread-prefix = "NettyServerBizHandler"shareBossWorker = falseclientSelectorThreadPrefix = "NettyClientSelector"clientSelectorThreadSize = 1clientWorkderThreadPrefix = "NettyClientWorkerThread"bossThreadSize = 1workerThreadSize = "default"}shutdown {wait = 3}serialzation = "seata"compressor = "none"
}
service {vgroupMapping.my_test_tx_group = "seata-server" # 这里的my_test_tx_group与application.yml中保持一致seata-server.grouplist = "127.0.0.1:8091"enableDegrade = falsedisableGlobalTransaction = false
}
client {rm {asyncCommitBufferLimit = 10000lock {retryInterval = 10retryTimes = 30retryPolicyBranchRollbackConflict = true}reportRetryCount = 5tableMetaCheckEnable = falsereportSuccessEnable = false}tm {commitRetryCount = 5rollbackRetryCount = 5}undo {dataValidation = truelogSerialization = "jackson"logTable = "undo_log"}log {exceptionRate = 100}
}
*** registry.conf
registry {# 可选用的类型file 、nacos 、eureka、redis、zk、consul、etcd3、sofatype = "eureka"nacos {application = "seata-server"serverAddr = "localhost"namespace = ""cluster = "default"username = ""password = ""}eureka {serviceUrl = "http://localhost:8801/eureka"application = "seata-server"weight = "1"}redis {serverAddr = "localhost:6379"db = 0password = ""cluster = "default"timeout = 0}zk {cluster = "default"serverAddr = "127.0.0.1:2181"sessionTimeout = 6000connectTimeout = 2000username = ""password = ""}consul {cluster = "default"serverAddr = "127.0.0.1:8500"}etcd3 {cluster = "default"serverAddr = "http://localhost:2379"}sofa {serverAddr = "127.0.0.1:9603"application = "default"region = "DEFAULT_ZONE"datacenter = "DefaultDataCenter"cluster = "default"group = "SEATA_GROUP"addressWaitTime = "3000"}file {name = "file.conf"}
}
config {# file、nacos 、apollo、zk、consul、etcd3、springCloudConfigtype = "file"nacos {serverAddr = "localhost"namespace = ""group = "SEATA_GROUP"username = ""password = ""}consul {serverAddr = "127.0.0.1:8500"}apollo {appId = "seata-server"apolloMeta = "http://192.168.1.204:8801"namespace = "application"}zk {serverAddr = "127.0.0.1:2181"sessionTimeout = 6000connectTimeout = 2000username = ""password = ""}etcd3 {serverAddr = "http://localhost:2379"}file {name = "file.conf"}
}
*** application.yml
server:port: 8802
spring:application:name: seata-ordercloud:alibaba:seata:# 这里要与file.conf中的值保持一致tx-service-group: my_test_tx_groupdatasource:driver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: 123456url: jdbc:mysql://locahost:3306/seata_order?serverTimezone=GMT%2B8&characterEncoding=utf8eureka:client:service-url:dafaultZone: http://localhost:8801/eureka/instance:hostname: localhostprefer-ip-address: true
logging:level:io:seata: info
mybatis:mapperLocations: classpath:mapper/*.xml
*** 启动类
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
@EnableEurekaClients
public class OrderAppMain8802
{public static void main(String[] args){SpringApplication.run(OrderAppMain8802.class, args);}
}
*** config
**** DataSourceProxyConfig.java
@Configuration
public class DataSourceProxyConfig {@Value("${mybatis.mapperLocations}")private String mapperLocations;@Bean@ConfigurationProperties(prefix = "spring.datasource")public DataSource druidDataSource() { return new DruidDataSource(); }@Beanpublic DataSourceProxy dataSourceProxy(DataSource dataSource) { return new DataSourceProxy(dataSource); }@Beanpublic SqlSessionFactory sqlSessionFactory(DataSourceProxy dataSourceProxy) throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSourceProxy);sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());return sqlSessionFactoryBean.getObject();}
}
**** MyBatisConfig.java
@Configuration
@MapperScan({"com.seata.order.dao"})
public class MyBatisConfig{
}
*** entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {private Long id;private Long userId;private Long productId;private Integer count;private BigDecimal money;private Integer status;private Date gmtCreate;private Date gmtModified;
}
*** controller
@RequestController
@RequestMapping("/order/")
public class OrderController {@Resourceprivate OrderService orderService;@GetMapping("create")public String create(Order order){orderService.create(order);return "订单创建成功,库存扣减成功,账户余额扣减成功"}
}
*** service & serviceImpl
**** service
public interface OrderService {void create(Order order);
}
@FeignClient(name = "account-service")
public interface AccountService {@GetMapping(value = "/account/decrease")String decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money);
}
@FeignClient(name = "storage-service")
public interface StorageService {@GetMapping(value = "/storage/decrease")String decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count);
}
**** serviceImpl
@Service
public class OrderServiceImpl implements OrderService {@Resourceprivate OrderDao orderDao;@Resourceprivate StorageService storageService;@Resourceprivate AccountService accountService;// 这个注解很重要@GlobalTransactional(name = "app-create-order", rollback = Exception.class)public void create(Order order) {orderDao.create(order);storageService.decrease(order.getProductId(), order.getCount());accountService.decrease(order.getUserId(), order.getMoney());orderDao.update(order.getUserId(), order.getMoney(), 0);}
}
*** dao
@Mapper
public interface OrderDao {void create(Order order);
}
*** resources
**** mapper.OrderMapper.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.seata.order.dao.OrderDao"><resultMap id="BaseResultMap" type="com.seata.order.entity.Order"><id column="id" property="id" jdbcType="BIGINT"><id column="user_id" property="userId" jdbcType="BIGINT"><id column="product_id" property="productId" jdbcType="BIGINT"><id column="count" property="count" jdbcType="INTEGER"><id column="money" property="money" jdbcType="DECIMAL"><id column="status" property="staus" jdbcType="INTEGER"><id column="gmt_create" property="" jdbcType="DATE"><id column="gmt_modified" property="" jdbcType="DATE"></resultMap><insert id="create">insert into orders(user_id, product_id, count, money, status)values (#{userId}, #{productId}, #{count}, #{money}, 0)</insert>
</mapper>
未完待续~~
初识Seata(二)相关推荐
- 初识JavaScript(二)
初识JavaScript(二) 我从上一篇<初识JavaScript(一)>知道和认识JavaScript的词法结构,也开始慢慢接触到了JavaScript的使用方法,是必须按照JavaS ...
- 初识react(二) 实现一个简版的html+redux.js的demo
回顾 初识react(一) 揭开jsx语法和虚拟DOM面纱 初识react(二) 实现一个简版的html+redux.js的demo 初识react(三)在 react中使用redux来实现简版计数器 ...
- android模仿qq登录界面,初识Android二之小试牛刀模仿实现qq登陆界面
初识Android二之小试牛刀模仿实现qq登陆界面.俗话说得好,老师踢开门,修行在自己.勉勉强强学完生命周期,然后悠悠闲闲听了两节课后,老师就布置了一个登陆界面的实现,于是,磕磕绊绊的修行之路开始了. ...
- RTKLIB专题学习(四)---单点定位实现初识(二)
RTKLIB专题学习(四)-单点定位实现初识(二) 今天我们来继续学习RTKLIB中单点定位的调用情况,上一篇在这里:RTKLIB专题学习(四)-单点定位实现初识(一) 1.上篇说到了调用procpo ...
- Python正则表达式初识(二)
前几天给大家分享了[Python正则表达式初识(一)],介绍了正则表达式中的三个特殊字符"^"."."和"*",感兴趣的伙伴可以戳进去看看, ...
- python正则表达式初识(七)_Python正则表达式初识(二)
前几天给大家分享了Python正则表达式初识(一),介绍了正则表达式中的三个特殊字符"^"."."和"*",感兴趣的伙伴可以戳进去看看,今天 ...
- seata(二) 分布式事务框架seata1.3 AT及XA模式实例演示
欢迎关注本人公众号 概述 Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务.Seata 将为用户提供了 AT.TCC.SAGA 和 XA 事务模式,为用户打造一站 ...
- 初识Java ~ (二) # Java 中程序的执行流程,(万字长文)特别细~ 可收藏~
大家好~ ,我是 清汉 时隔一月,懒惰的我终于将 Java 初识系列文章,第二篇程序执行流程码出来了~ 初识Java ~ (一) 从0开始,Java基础知识,双手奉上~ 可收藏!! 应该不算太晚吧,这 ...
- 初识 QR 二维码(零)
提到二维码,想必大家每天都会接触到,扫码支付.扫码添加微信好友等都会用到.关于二维码的生成原理,网上确实有些介绍,但基本涉及到具体编码就一笔带过没有深入了.目前 Python 也有现成的模块可以调用来 ...
最新文章
- python 执行vba脚本_用python批量执行VBA代码
- setup.py安装
- pycharm 自动生成文件注释和函数注释
- 免费Opengrok-代码阅读工具:Kernel,Optee,ATF,Uboot...
- c++ 一个函数包括多个返回值判断_轻松玩转函数式编程
- jenkins再不同操作系统上的安装教程
- 高级数据结构与算法 | AVL树 (高度平衡树)
- javascript日历插件
- BCVP开发者说第3期:Adnc
- 最短路中部分点只能从中任意选取K个问题
- sharepoint小 tip
- 如何让一个函数返回多个值(C#)
- 时序图数仓AbutionGraph场景应用
- ABB机器人切割铣削钻孔自动化加工应用
- 《算法导论》常见算法总结
- 3月21日短线黑马牛股公开验证
- 【Nowcoder】2020牛客暑期多校训练营(第八场)I - Interesting Computer Game | 并查集、思维、离散化
- ubuntu 打开和拔出U盘
- 如何使用手机远程访问自己的电脑?
- 经济学为什么不考虑沉没成本?
热门文章
- NLP基础——python的jieba用于词类分割用法总结(1)
- 中国合成树脂行业“十三五”发展回顾及“十四五”前景展望「图」
- 响应“十三五”汽车工业发展规划意见,景联文提供自动驾驶全场景定制化数据采集标注服务
- 城市绿色配送信息服务平台方案
- sublime的vscode主题_谈下sublime和vscode
- 容联?融云?环信?开发者怎么选
- laravel5.2总结--服务容器(依赖注入,控制反转)
- python基础课程1
- 金蝶房地产行业解决方案
- Python 爬取天天基金各基金日期、净值、日增长率数据