> 文档中心 > SpringCloudAlibaba之Seata微服务之间调用(七)

SpringCloudAlibaba之Seata微服务之间调用(七)

上一节介绍seata的安装。本节主要讲述使用seata实现分布式事务。

创建库和表

创建一个业务需要的数据库,例如seata_order。接下来是是创建表。

为了演示业务,创建三种表分别是order(订单表)、storage(商品库存表)、user_account(用户账号信息表)。

-- seata_order.user_account definitionCREATE TABLE `user_account` (  `userid` bigint(20) NOT NULL AUTO_INCREMENT,  `account` bigint(20) DEFAULT NULL,  `name` varchar(64) DEFAULT NULL,  PRIMARY KEY (`userid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;-- seata_order.storage definitionCREATE TABLE `storage` (  `commoditycode` bigint(20) NOT NULL AUTO_INCREMENT,  `name` varchar(64) DEFAULT NULL,  `count` bigint(20) DEFAULT NULL,  `price` bigint(20) DEFAULT NULL,  PRIMARY KEY (`commoditycode`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;-- seata_order.`order` definitionCREATE TABLE `order` (  `orderid` bigint(20) NOT NULL AUTO_INCREMENT,  `commoditycode` bigint(20) DEFAULT NULL,  `userId` bigint(20) DEFAULT NULL,  `num` bigint(20) DEFAULT NULL,  `money` bigint(20) DEFAULT NULL,  `status` bigint(20) DEFAULT NULL,  PRIMARY KEY (`orderid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;

storage(商品库存表)和user_account(用户账号信息表)造几条数据备用。自己可以根据自己的喜好自己造,一下是我创建的几条数据,仅供参考。

  

这里还需要一张seata相关的表,跟业务表放在同一个数据库下,表结构如下

-- the table to store seata xid data-- 0.7.0+ add context-- you must to init this sql for you business databese. the seata server not need it.-- 此脚本必须初始化在你当前的业务数据库中,用于AT 模式XID记录。与server端无关(注:业务数据库)-- 注意此处0.3.0+ 增加唯一索引 ux_undo_logdrop table `undo_log`;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;

商品微服务Goods

pom核心内容

      org.springframework.boot     spring-boot-starter-web       org.springframework.boot     spring-boot-starter-actuator        com.alibaba.cloud     spring-cloud-starter-alibaba-nacos-discovery             com.google.code.findbugs      jsr305          org.hdrhistogram      HdrHistogram          org.checkerframework      checker-qual          com.google.errorprone      error_prone_annotations              com.alibaba.cloud     spring-cloud-starter-alibaba-seata        org.mybatis.spring.boot     mybatis-spring-boot-starter     ${mybatis.springboot.version}             org.springframework.boot      spring-boot-starter-logging               mysql     mysql-connector-java        com.alibaba     druid-spring-boot-starter     ${druid.springboot.version}     

application.yml相关配置

server:  port: 7002spring:  profiles:    active: dev  application:    name: GOODS  cloud:    nacos:      discovery: namespace: ec6004af-f122-4ed1-b6d6-5d77ea5d5c94 group: SEATA_GROUP      server-addr: 192.168.43.85:8845,192.168.43.229:8846,192.168.43.251:8847    alibaba:      seata: tx-service-group: my_test_tx_groupmybatis:  type-aliases-package: com.juwusheng.goods.model  mapper-locations: classpath:mapper/*.xmlribbon:  eager-load:    enabled: true#spring cloud alibaba 2.1.4 之后支持yml中配置seata属性,可以用来替换registry.conf文件seata:  # seata 服务分组,要与服务端nacos-config.txt中service.vgroup_mapping的后缀对应  tx-service-group: my_test_tx_group  registry:    # 指定nacos作为注册中心    type: nacos    nacos:      server-addr: 192.168.43.85:8845,192.168.43.229:8846,192.168.43.251:8847      application: seata-server      group: SEATA_GROUP      namespace: ec6004af-f122-4ed1-b6d6-5d77ea5d5c94  config:    # 指定nacos作为配置中心    type: nacos    nacos:      server-addr: 192.168.43.85:8845,192.168.43.229:8846,192.168.43.251:8847      namespace: ec6004af-f122-4ed1-b6d6-5d77ea5d5c94      group: SEATA_GROUP  service:    vgroup-mapping:      my_test_tx_group: default    disable-global-transaction: false  client:    rm:      report-success-enable: false

上述主要是配置关于seata客户端注册到nacos先关配置。这里还增加了一个application-dev.yml的配置,主要配置数据库。

spring:  datasource:    username: 账号    password: 密码    url: jdbc:mysql://127.0.0.1:3306/seata_order?characterEncoding=utf8&useSSL=false    driver-class-name: com.mysql.cj.jdbc.Driver    type: com.alibaba.druid.pool.DruidDataSource    druid:      initial-size: 5      min-idle: 5      max-wait: 60000      max-active: 20      validation-query: SELECT 1      stat-view-servlet: #启动控制平台 login-username: druid login-password: druid enabled: true      filter: stat:   slow-sql-millis: 1   log-slow-sql: true log4j2:   enabled: true   exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"      filters: stat,wall,log4j2

 这里使用阿里druid的连接池,数据账号密码以及连接请更改。

主函数SpringBootApplication注解中剔除DataSourceAutoConfiguration。如下

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})

手动配置数据库启动类MybatisConfig

package com.juwusheng.goods.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.annotation.MapperScan;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.context.annotation.Primary;import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import org.springframework.core.io.support.ResourcePatternResolver;import javax.sql.DataSource;@Configuration@MapperScan("com.juwusheng.goods.dao")public class MybatisConfig {    @Value("${mybatis.mapper-locations:classpath:mapper/*.xml}")    String mybatisMapperLocation;    /     * 从配置文件获取属性构造datasource,注意前缀,这里用的是druid,根据自己情况配置,     * 原生datasource前缀取"spring.datasource"     */    @Bean    @ConfigurationProperties(prefix = "spring.datasource")    public DataSource druidDataSource() { DruidDataSource druidDataSource = new DruidDataSource(); return druidDataSource;    }//    ///     * 构造datasource代理对象,替换原来的datasource//     * @param druidDataSource//    @Primary//    @Bean("dataSource")//    public DataSourceProxy dataSourceProxy(DataSource druidDataSource) {// return new DataSourceProxy(druidDataSource);//    }*/    @Bean(name = "sqlSessionFactory")    public SqlSessionFactory sqlSessionFactoryBean(DataSource druidDataSource) throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); //设置代理数据源 factoryBean.setDataSource(new DataSourceProxy(druidDataSource)); ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); factoryBean.setMapperLocations(resolver.getResources(mybatisMapperLocation)); org.apache.ibatis.session.Configuration configuration=new org.apache.ibatis.session.Configuration(); //使用jdbc的getGeneratedKeys获取数据库自增主键值 configuration.setUseGeneratedKeys(true); //使用列别名替换列名 configuration.setUseColumnLabel(true); //自动使用驼峰命名属性映射字段,如userId ---> user_id configuration.setMapUnderscoreToCamelCase(true); //更改默认的事务 factoryBean.setTransactionFactory(new SpringManagedTransactionFactory()); factoryBean.setConfiguration(configuration); return factoryBean.getObject();    }}

因涉及三个微服务,代码较多,忽略部分非核心代码,比如数据库表对应的java的model对象类。

新增一个Controller的类,增加请求代码。

@PostMapping("/goods/deductStorage")    public String deductStorage(@RequestParam(value="id",defaultValue = "0") String id,@RequestParam(value="count",defaultValue = "0") String count) throws Exception { boolean isSuccess=goodsService.deductStorage(id,count); Map map= new HashMap(8); map.put("code",isSuccess?"ok":"fail"); map.put("msg",isSuccess?"成功":"失败"); return jsonUtil.objectToJson(map);    }

goodsService的核心代码

public boolean deductStorage(String id, String count) throws Exception { logger.info("事务XID:{}", RootContext.getXID()); Storage storage = storageMapper.selectByPrimaryKey(Long.parseLong(id)); if (storage.getCount() 0 ? true: false;    }

Mapper.xml中减少商品库存核心的sql

     update storage    set count = count - #{count,jdbcType=BIGINT}    where commoditycode = #{id,jdbcType=BIGINT}  

由于篇幅过长,下一节说明剩余的两个微服务的代码。