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

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

创建库和表

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

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

-- seata_order.user_account definition

CREATE 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 definition

CREATE 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` definition

CREATE 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_log
drop 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;

表中datetime类型改成timestamp,有疑问,请查阅上一节

商品微服务Goods

pom核心内容

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--引入nacos client的依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>com.google.code.findbugs</groupId>
                    <artifactId>jsr305</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.hdrhistogram</groupId>
                    <artifactId>HdrHistogram</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.checkerframework</groupId>
                    <artifactId>checker-qual</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.google.errorprone</groupId>
                    <artifactId>error_prone_annotations</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
        </dependency>

        <!--  mybatis集成封装-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis.springboot.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--mysql驱动包-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- 阿里巴巴连接池Druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.springboot.version}</version>
        </dependency>
    </dependencies>

application.yml相关配置

server:
  port: 7002
spring:
  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_group
mybatis:
  type-aliases-package: com.juwusheng.goods.model
  mapper-locations: classpath:mapper/*.xml
#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的配置,主要配置数据库,application-dev.yml放在和application.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<String,String> 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()<Long.parseLong(count)){
            throw new Exception("库存不足");
        }
        int rowCount = storageMapper.storageReduce(Long.parseLong(id),Long.parseLong(count));

        return rowCount > 0 ? true: false;
    }

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

 <update id="storageReduce" parameterType="java.lang.Long">
    update storage
    set count = count - #{count,jdbcType=BIGINT}
    where commoditycode = #{id,jdbcType=BIGINT}
  </update>

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值