目录

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
  • 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(二)相关推荐

  1. 初识JavaScript(二)

    初识JavaScript(二) 我从上一篇<初识JavaScript(一)>知道和认识JavaScript的词法结构,也开始慢慢接触到了JavaScript的使用方法,是必须按照JavaS ...

  2. 初识react(二) 实现一个简版的html+redux.js的demo

    回顾 初识react(一) 揭开jsx语法和虚拟DOM面纱 初识react(二) 实现一个简版的html+redux.js的demo 初识react(三)在 react中使用redux来实现简版计数器 ...

  3. android模仿qq登录界面,初识Android二之小试牛刀模仿实现qq登陆界面

    初识Android二之小试牛刀模仿实现qq登陆界面.俗话说得好,老师踢开门,修行在自己.勉勉强强学完生命周期,然后悠悠闲闲听了两节课后,老师就布置了一个登陆界面的实现,于是,磕磕绊绊的修行之路开始了. ...

  4. RTKLIB专题学习(四)---单点定位实现初识(二)

    RTKLIB专题学习(四)-单点定位实现初识(二) 今天我们来继续学习RTKLIB中单点定位的调用情况,上一篇在这里:RTKLIB专题学习(四)-单点定位实现初识(一) 1.上篇说到了调用procpo ...

  5. Python正则表达式初识(二)

    前几天给大家分享了[Python正则表达式初识(一)],介绍了正则表达式中的三个特殊字符"^"."."和"*",感兴趣的伙伴可以戳进去看看, ...

  6. python正则表达式初识(七)_Python正则表达式初识(二)

    前几天给大家分享了Python正则表达式初识(一),介绍了正则表达式中的三个特殊字符"^"."."和"*",感兴趣的伙伴可以戳进去看看,今天 ...

  7. seata(二) 分布式事务框架seata1.3 AT及XA模式实例演示

    欢迎关注本人公众号 概述 Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务.Seata 将为用户提供了 AT.TCC.SAGA 和 XA 事务模式,为用户打造一站 ...

  8. 初识Java ~ (二) # Java 中程序的执行流程,(万字长文)特别细~ 可收藏~

    大家好~ ,我是 清汉 时隔一月,懒惰的我终于将 Java 初识系列文章,第二篇程序执行流程码出来了~ 初识Java ~ (一) 从0开始,Java基础知识,双手奉上~ 可收藏!! 应该不算太晚吧,这 ...

  9. 初识 QR 二维码(零)

    提到二维码,想必大家每天都会接触到,扫码支付.扫码添加微信好友等都会用到.关于二维码的生成原理,网上确实有些介绍,但基本涉及到具体编码就一笔带过没有深入了.目前 Python 也有现成的模块可以调用来 ...

最新文章

  1. python 执行vba脚本_用python批量执行VBA代码
  2. setup.py安装
  3. pycharm 自动生成文件注释和函数注释
  4. 免费Opengrok-代码阅读工具:Kernel,Optee,ATF,Uboot...
  5. c++ 一个函数包括多个返回值判断_轻松玩转函数式编程
  6. jenkins再不同操作系统上的安装教程
  7. 高级数据结构与算法 | AVL树 (高度平衡树)
  8. javascript日历插件
  9. BCVP开发者说第3期:Adnc
  10. 最短路中部分点只能从中任意选取K个问题
  11. sharepoint小 tip
  12. 如何让一个函数返回多个值(C#)
  13. 时序图数仓AbutionGraph场景应用
  14. ABB机器人切割铣削钻孔自动化加工应用
  15. 《算法导论》常见算法总结
  16. 3月21日短线黑马牛股公开验证
  17. 【Nowcoder】2020牛客暑期多校训练营(第八场)I - Interesting Computer Game | 并查集、思维、离散化
  18. ubuntu 打开和拔出U盘
  19. 如何使用手机远程访问自己的电脑?
  20. 经济学为什么不考虑沉没成本?

热门文章

  1. NLP基础——python的jieba用于词类分割用法总结(1)
  2. 中国合成树脂行业“十三五”发展回顾及“十四五”前景展望「图」
  3. 响应“十三五”汽车工业发展规划意见,景联文提供自动驾驶全场景定制化数据采集标注服务
  4. 城市绿色配送信息服务平台方案
  5. sublime的vscode主题_谈下sublime和vscode
  6. 容联?融云?环信?开发者怎么选
  7. laravel5.2总结--服务容器(依赖注入,控制反转)
  8. python基础课程1
  9. 金蝶房地产行业解决方案
  10. Python 爬取天天基金各基金日期、净值、日增长率数据