SA实战 ·《SpringCloud Alibaba实战》开撸:完成通用模块的开发

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: SA实战 ·《SpringCloud Alibaba实战》开撸:完成通用模块的开发

项目总体结构

项目总体上包含一个Maven父工程,实体类模块、工具类模块、用户微服务、商品微服务和订单微服务都以Maven子模块的形式存在,项目总体结果如下所示。

图片.png

其中每个部分的含义如下所示。

其中各模块的说明如下所示:

  • shop-springcloud-alibaba:Maven父工程。
  • shop-bean:各服务都会使用的JavaBean模块,包含实体类、Dto、Vo等JavaBean。
  • shop-utils:各服务都会使用的工具类模块。
  • shop-order:订单微服务,监听的端口为8080。
  • shop-product:商品微服务,监听的端口为8070。
  • shop-user:用户微服务,监听的端口为8060。

创建Maven父工程

这里,我使用的开发环境是大家都比较熟悉的IDEA,关于IDEA的使用,这里我就不再赘述了,如果有对IDEA的使用不太熟悉的小伙伴,那就自行百度或者谷歌吧,今天我们先重点撸源码。

在IDEA中创建Maven工程,名称为shop-springcloud-alibaba,创建后在项目的pom.xml文件中添加StringBoot与SpringCloud alibaba相关的配置,如下所示。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.6.RELEASE</version>
</parent>
<properties>
    <java.version>1.8</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
    <spring-cloud-alibaba.version>2.1.0.RELEASE</spring-cloud-alibaba.version>
    <logback.version>1.1.7</logback.version>
    <slf4j.version>1.7.21</slf4j.version>
    <common.logging>1.2</common.logging>
    <fastjson.version>1.2.51</fastjson.version>
    <mybatis.version>3.4.6</mybatis.version>
    <mybatis.plus.version>3.4.1</mybatis.plus.version>
    <mysql.jdbc.version>8.0.19</mysql.jdbc.version>
    <druid.version>1.1.10</druid.version>
</properties>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>${spring-cloud-alibaba.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

创建工具类模块

在父工程下创建工具类模块shop-utils,作为整个项目的通用工具类模块。工具类模块的总体结构如下所示。

图片.png


添加项目依赖

在shop-utils模块的pom.xml文件中添加项目依赖的一些类库,如下所示。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.1</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>${mysql.jdbc.version}</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>${druid.version}</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>${druid.version}</version>
    </dependency>
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>${common.logging}</version>
    </dependency>
    <!-- log -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${slf4j.version}</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>${logback.version}</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>${fastjson.version}</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.8</version>
    </dependency>
</dependencies>

核心类开发

1.创建HTTP状态码封装类

在项目的io.binghe.shop.utils.constants包下创建HttpCode类,作为HTTP状态码的常量类。这里,暂时定义了两个状态码,200表示处理成功,500表示服务器异常,源码如下所示。

/**
 * @author binghe
 * @version 1.0.0
 * @description http状态码
 */
public class HttpCode {
    /**
     * 成功的状态码
     */
    public static final int SUCCESS = 200;
    /**
     * 错误状态码
     */
    public static final int FAILURE = 500;
}

2.创建全局异常捕获类

在项目的io.binghe.shop.utils.exception包下新建全局异常捕获类RestCtrlExceptionHandler,统一捕获整个项目抛出的Exception异常,源码如下所示。

/**
 * @author binghe
 * @version 1.0.0
 * @description 全局异常处理器
 */
@RestControllerAdvice
public class RestCtrlExceptionHandler {
 private final Logger logger = LoggerFactory.getLogger(RestCtrlExceptionHandler.class);
 /**
  * 全局异常处理,统一返回状态码
  */
 @ExceptionHandler(Exception.class)
 public Result<String> handleException(Exception e) {
  logger.error("服务器抛出了异常:{}", e);
  return new Result<String>(HttpCode.FAILURE, "执行失败", e.getMessage());
 }
}

3.创建通用MD5与密码加密类

在io.binghe.shop.utils.md5包下新建MD5Hash类,提供通用的MD5加密算法,在io.binghe.shop.utils.psswd包下新建PasswordUtils类,提供密码的加密功能。这两个类的实现比较简单,这里就不再赘述了。感兴趣的小伙伴加入 【冰河技术】 知识星球获取源码。

4.创建通用数据响应类

在项目的io.binghe.shop.utils.resp包下新建Result类,用于封装统一的数据返回格式,源码如下所示。

/**
 * @author binghe
 * @version 1.0.0
 * @description 返回的结果数据
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> implements Serializable {
    private static final long serialVersionUID = 1497405107265595284L;
    /**
     * 状态码
     */
    private Integer code;
    /**
     * 状态描述
     */
    private String codeMsg;
    /**
     *  返回的数据
     */
    private T data;
}

这里,需要注意的是:在Result类中使用了泛型,返回的具体业务数据类型会根据泛型的具体类型确定。Result类中的每个字段的含义如下所示。

  • code:返回的状态码。
  • codeMsg:返回的状态描述信息。
  • data:具体的业务数据,数据类型根据泛型确定。

5.创建分布式id核心类

(1)在项目的io.binghe.shop.utils.id包下创建实现整个分布式id最核心的类SnowFlake,SnowFlake类主要是使用Java实现了雪花算法,具体的逻辑见如下源码。

/**
 * @author binghe
 * @version 1.0.0
 * @description 雪花算法生成分布式序列号
 */
public class SnowFlake {
    /**
     * 起始的时间戳:2022-04-12 11:56:45,使用时此值不可修改
     */
    private final static long START_STAMP = 1649735805910L;
    /**
     * 每一部分占用的位数
     */
    private final static long SEQUENCE_BIT   = 12; //序列号占用的位数
    private final static long MACHINE_BIT    = 5;   //机器标识占用的位数
    private final static long DATACENTER_BIT = 5;//数据中心占用的位数
    /**
     * 每一部分的最大值
     */
    private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
    private final static long MAX_MACHINE_NUM    = -1L ^ (-1L << MACHINE_BIT);
    private final static long MAX_SEQUENCE       = -1L ^ (-1L << SEQUENCE_BIT);
    /**
     * 每一部分向左的位移
     */
    private final static long MACHINE_LEFT    = SEQUENCE_BIT;
    private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    private final static long TIMESTMP_LEFT   = DATACENTER_LEFT + DATACENTER_BIT;
    private long datacenterId;  //数据中心
    private long machineId;     //机器标识
    private long sequence = 0L; //序列号
    private long lastStmp = -1L;//上一次时间戳
    public SnowFlake(long datacenterId, long machineId) {
        if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
            throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
        }
        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
            throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
        }
        this.datacenterId = datacenterId;
        this.machineId = machineId;
    }
    /**
     * 产生下一个ID
     */
    public synchronized long nextId() {
        long currStmp = getNewstmp();
        if (currStmp < lastStmp) {
            throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
        }
        if (currStmp == lastStmp) {
            //相同毫秒内,序列号自增
            sequence = (sequence + 1) & MAX_SEQUENCE;
            //同一毫秒的序列数已经达到最大
            if (sequence == 0L) {
                currStmp = getNextMill();
            }
        } else {
            //不同毫秒内,序列号置为0
            sequence = 0L;
        }
        lastStmp = currStmp;
        return (currStmp - START_STAMP) << TIMESTMP_LEFT //时间戳部分
                | datacenterId << DATACENTER_LEFT       //数据中心部分
                | machineId << MACHINE_LEFT             //机器标识部分
                | sequence;                             //序列号部分
    }
    private long getNextMill() {
        long mill = getNewstmp();
        while (mill <= lastStmp) {
            mill = getNewstmp();
        }
        return mill;
    }
    private long getNewstmp() {
        return System.currentTimeMillis();
    }
    public static Long getMaxDataCeneterNum() {
     return MAX_DATACENTER_NUM;
    }
    public static Long getMaxMachineNum() {
     return MAX_MACHINE_NUM;
    }
}

根据雪花算法的实现可以发现,SnowFlake类提供了一个有参构造函数,如下所示。

public SnowFlake(long datacenterId, long machineId) {
    if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
        throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
    }
    if (machineId > MAX_MACHINE_NUM || machineId < 0) {
        throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
    }
    this.datacenterId = datacenterId;
    this.machineId = machineId;
}

其中,第一个参数datacenterId表示数据中心id,也可以认为是机房的id,machineId表示机器id,也可以认为是服务所在的服务器id。在SnowFlake的构造方法中,对这两个参数进行了限制,如下所示。

  • datacenterId:大于或者等于0,并且要小于MAX_DATACENTER_NUM,也就是小于31。
  • machineId:大于或者等于0,并且要小于MAX_DATACENTER_NUM,也就是小于31。

所以,类实现的雪花算法支持32个不同的数据中心或机房,并且在每个数据中心或机房中支持32个机器上部署分布式id服务。这对一般的场景来说,已经足够了。

注意:雪花算法的实现强依赖时间戳,所以在SnowFlake源码中存在如下常量,并标注了使用时此值不可更改的注释。

/**
 * 起始的时间戳:2022-04-12 11:56:45,使用时此值不可修改
 */
private final static long START_STAMP = 1649735805910L;

有关雪花算法的核心原理,以及如何实现在分布式场景下做到id唯一并且整体呈现递增趋势,会在后续的拓展篇中详细介绍,这里就不再赘述了,我们先把工具类和实体类的源码撸完。

(2)为了防止每次使用SnowFlake类时都会新建一个对象,这里,在io.binghe.shop.utils.id包下新建SnowFlakeFactory类,作为SnowFlake的简单工厂类,在SnowFlakeFactory类中,主要是定义了一个ConcurrentMap类型的成员变量snowFlakeCache用来缓存SnowFlake类的对象,这样就不用在使用SnowFlake类时,每次都要新建一个类对象了。

也许有小伙伴会问:不就是新建一个对象嘛,为啥还要缓存起来呢。

其实,在普通场景下,新建不新建对象,缓存不缓存对象几乎没啥影响,但是在高并发、大流量的场景下,尤其是冰河经历过了高并发、大流量的秒杀系统,如果每次都创建对象的话,系统的性能与资源损耗还是比较大的。

SnowFlakeFactory类的源码如下所示。

/**
 * @author binghe
 * @version 1.0.0
 * @description 雪花算法工厂
 */  
public class SnowFlakeFactory { 
 /**
  * 默认数据中心id
  */
 private static final long DEFAULT_DATACENTER_ID = 1;
 /**
  * 默认的机器id
  */
 private static final long DEFAULT_MACHINE_ID = 1;
 /**
  * 默认的雪花算法句柄
  */
 private static final String DEFAULT_SNOW_FLAKE = "snow_flake";
 /**
  * 缓存SnowFlake对象
  */
 private static ConcurrentMap<String, SnowFlake> snowFlakeCache = new ConcurrentHashMap<>(2);
 public static SnowFlake getSnowFlake(long datacenterId, long machineId) {
  return new SnowFlake(datacenterId, machineId);
 }
 public static SnowFlake getSnowFlake() {
  return new SnowFlake(DEFAULT_DATACENTER_ID, DEFAULT_MACHINE_ID);
 }
 public static SnowFlake getSnowFlakeFromCache() {
  SnowFlake snowFlake = snowFlakeCache.get(DEFAULT_SNOW_FLAKE);
  if(snowFlake == null) {
   snowFlake = new SnowFlake(DEFAULT_DATACENTER_ID, DEFAULT_MACHINE_ID);
   snowFlakeCache.put(DEFAULT_SNOW_FLAKE, snowFlake);
  }
  return snowFlake;
 }
 /**
  * 根据数据中心id和机器id从缓存中获取全局id
  * @param dataCenterId: 取值为1~31
  * @param machineId: 取值为1~31
  */
 public static SnowFlake getSnowFlakeByDataCenterIdAndMachineIdFromCache(Long dataCenterId, Long machineId) {
   if (dataCenterId > SnowFlake.getMaxDataCeneterNum() || dataCenterId < 0) {
            throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
        }
        if (machineId > SnowFlake.getMaxMachineNum() || machineId < 0) {
            throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
        }
  String key = DEFAULT_SNOW_FLAKE.concat("_").concat(String.valueOf(dataCenterId)).concat("_").concat(String.valueOf(machineId));
  SnowFlake snowFlake = snowFlakeCache.get(key);
  if(snowFlake == null) {
   snowFlake = new SnowFlake(dataCenterId, machineId);
   snowFlakeCache.put(key, snowFlake);
  }
  return snowFlake;
 }
}

在SnowFlakeFactory类中,主要对外提供了两个获取SnowFlake的方法,一个是getSnowFlakeFromCache()方法,另一个是getSnowFlakeByDataCenterIdAndMachineIdFromCache()方法。

  • getSnowFlakeFromCache()方法

在snowFlakeCache缓存中获取默认的SnowFlake对象实例,如果对象不存在,则调用SnowFlake类的构造方法,并且传入默认的数据中心id和机器id,将实例化后的SnowFlake对象加入缓存,并且返回SnowFlake对象。源码如下所示。

public static SnowFlake getSnowFlakeFromCache() {
    SnowFlake snowFlake = snowFlakeCache.get(DEFAULT_SNOW_FLAKE);
    if(snowFlake == null) {
        snowFlake = new SnowFlake(DEFAULT_DATACENTER_ID, DEFAULT_MACHINE_ID);
        snowFlakeCache.put(DEFAULT_SNOW_FLAKE, snowFlake);
    }
    return snowFlake;
}
  • getSnowFlakeByDataCenterIdAndMachineIdFromCache()方法

getSnowFlakeByDataCenterIdAndMachineIdFromCache()方法提供了两个参数,一个是Long类型的dataCenterId,表示数据中心或者机房的id,一个是Long类型的machineId,表示机器id或者服务所在的服务器id。

在getSnowFlakeByDataCenterIdAndMachineIdFromCache()方法中,会对传入的两个参数进行限制。然后生成缓存SnowFlake对象实例的缓存Key,根据生成的Key到snowFlakeCache缓存中获取SnowFlake对象实例,如果对象实例不存在,则根据传入的dataCenterId和machineId生成SnowFlake对象实例,并放入snowFlakeCache缓存中,最后返回SnowFlake对象实例。源码如下所示。

/**
 * 根据数据中心id和机器id从缓存中获取全局id
 * @param dataCenterId: 取值为1~31
 * @param machineId: 取值为1~31
 */
public static SnowFlake getSnowFlakeByDataCenterIdAndMachineIdFromCache(Long dataCenterId, Long machineId) {
    if (dataCenterId > SnowFlake.getMaxDataCeneterNum() || dataCenterId < 0) {
        throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
    }
    if (machineId > SnowFlake.getMaxMachineNum() || machineId < 0) {
        throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
    }
    String key = DEFAULT_SNOW_FLAKE.concat("_").concat(String.valueOf(dataCenterId)).concat("_").concat(String.valueOf(machineId));
    SnowFlake snowFlake = snowFlakeCache.get(key);
    if(snowFlake == null) {
        snowFlake = new SnowFlake(dataCenterId, machineId);
        snowFlakeCache.put(key, snowFlake);
    }
    return snowFlake;
}

(3)为了便于管理每个服务的dataCenterId和machineId,这里将每个服务的dataCenterId和machineId作为配置参数,后续也可以存储到Zookeeper或者Etcd等分布式配置中心。

所以,在io.binghe.shop.utils.id包下新建SnowFlakeLoader类,io.binghe.shop.utils.id.SnowFlakeLoader类的作用主要是加载classpath类路径下的snowflake/snowflake.properties文件,读取dataCenterId和machineId,SnowFlakeLoader类的源码如下所示。

/**
 * @author binghe
 * @version 1.0.0
 * @description 定义加载params.properties文件的工具类
 */
public class SnowFlakeLoader {
 public static final String DATA_CENTER_ID = "data.center.id";
 public static final String MACHINE_ID = "machine.id";
 private volatile static Properties instance;
    static {
        InputStream in = SnowFlakeLoader.class.getClassLoader().getResourceAsStream("snowflake/snowflake.properties");
        instance = new Properties();
        try {
            instance.load(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private static String getStringValue(String key){
        if(instance == null) return "";
        return instance.getProperty(key, "");
    }
    private static Long getLongValue(String key){
       String v = getStringValue(key);
       return (v == null || v.trim().isEmpty()) ? 0 : Long.parseLong(v);
    }
    public static Long getDataCenterId() {
     return getLongValue(DATA_CENTER_ID);
    }
    public static Long getMachineId() {
     return getLongValue(MACHINE_ID);
    }
}

(4)为了配合SnowFlakeLoader类读取配置文件中的内容,在项目的resources目录下新建snowflake目录,并在snowflake目录下新建snowflake.properties文件,snowflake.properties文件的内容如下所示。

data.center.id=1
machine.id=1

至此,我们项目的通用工具类模块就实现完毕了,后续在开发具体业务时,如果需要扩展,我们在一起扩展通用工具类模块。

另外,源码中提供了针对分布式id的测试用例,可以加入【冰河技术】知识星球获取源码。

创建实体类模块

在父工程下创建实体类模块shop-bean,作为整个项目的通用实体类模块,实体类模块的总体结构如下所示。

图片.png


添加项目依赖

shop-bean模块的依赖相对来说就比较简单了,只需要依赖shop-utils模块即可。在shop-bean模块的pom.xml文件中添加如下配置。

<dependencies>
    <dependency>
        <groupId>io.binghe.shop</groupId>
        <artifactId>shop-utils</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </dependency>
</dependencies>

核心类开发

对于shop-bean模块来说,主要的功能就是提供JavaBean,目前主要提供四个实体类,分别如下所示。

(1)io.binghe.shop.bean#User类,表示用户类,源码如下所示。

/**
 * @author binghe(冰河技术)
 * @version 1.0.0
 * @description 用户实体类
 */
@Data
@TableName("t_user")
public class User implements Serializable {
    private static final long serialVersionUID = -7032479567987350240L;
    /**
     * 数据id
     */
    @TableId(value = "id", type = IdType.INPUT)
    @TableField(value = "id", fill = FieldFill.INSERT)
    private Long id;
    /**
     * 用户名
     */
    @TableField("t_username")
    private String username;
    /**
     * 密码
     */
    @TableField("t_password")
    private String password;
    /**
     * 手机号
     */
    @TableField("t_phone")
    private String phone;
    /**
     * 地址
     */
    @TableField("t_address")
    private String address;
    public User(){
        this.id = SnowFlakeFactory.getSnowFlakeFromCache().nextId();
        //默认密码
        this.password = PasswordUtils.getPassowrd("123456");
    }
}

(2)io.binghe.shop.bean#Product类,表示商品类,源码如下所示。

/**
 * @author binghe
 * @version 1.0.0
 * @description 商品
 */
@Data
@TableName("t_product")
public class Product implements Serializable {
    private static final long serialVersionUID = -2907409980909070073L;
    /**
     * 数据id
     */
    @TableId(value = "id", type = IdType.INPUT)
    @TableField(value = "id", fill = FieldFill.INSERT)
    private Long id;
    /**
     * 商品名称
     */
    @TableField("t_pro_name")
    private String proName;
    /**
     * 商品价格
     */
    @TableField("t_pro_price")
    private BigDecimal proPrice;
    /**
     * 商品库存
     */
    @TableField("t_pro_stock")
    private Integer proStock;
    public Product(){
        this.id = SnowFlakeFactory.getSnowFlakeFromCache().nextId();
    }
}

(3)io.binghe.shop.bean#Order类,表示订单类,源码如下所示。

/**
 * @author binghe
 * @version 1.0.0
 * @description 订单
 */
@Data
@TableName("t_order")
public class Order implements Serializable {
    private static final long serialVersionUID = -2907409980909070073L;
    /**
     * 数据id
     */
    @TableId(value = "id", type = IdType.INPUT)
    @TableField(value = "id", fill = FieldFill.INSERT)
    private Long id;
    /**
     * 用户id
     */
    @TableField("t_user_id")
    private Long userId;
    /**
     * 用户名
     */
    @TableField("t_user_name")
    private String username;
    /**
     * 手机号
     */
    @TableField("t_phone")
    private String phone;
    /**
     * 地址
     */
    @TableField("t_address")
    private String address;
    /**
     * 商品价格(总价)
     */
    @TableField("t_total_price")
    private BigDecimal totalPrice;
    public Order(){
        this.id = SnowFlakeFactory.getSnowFlakeFromCache().nextId();
    }
}

(4)io.binghe.shop.bean#OrderItem类,表示订单条目类,源码如下所示。

/**
 * @author binghe
 * @version 1.0.0
 * @description 订单明细
 */
@Data
@TableName("t_order_item")
public class OrderItem implements Serializable {
    private static final long serialVersionUID = -1329173923755780293L;
    /**
     * 数据id
     */
    @TableId(value = "id", type = IdType.INPUT)
    @TableField(value = "id", fill = FieldFill.INSERT)
    private Long id;
    @TableField("t_order_id")
    private Long orderId;
    /**
     * 商品id
     */
    @TableField("t_pro_id")
    private Long proId;
    /**
     * 商品名称
     */
    @TableField("t_pro_name")
    private String proName;
    /**
     * 商品价格(单价)
     */
    @TableField("t_pro_price")
    private BigDecimal proPrice;
    /**
     * 购买数量
     */
    @TableField("t_number")
    private Integer number;
    public OrderItem(){
        this.id = SnowFlakeFactory.getSnowFlakeFromCache().nextId();
    }
}

注意:四个实体类都比较简单,小伙伴们可以直接看源码的注释,这里就不再赘述了。同时,这里是为简化商品的下单逻辑而创建的实体类,实际场景下的实体类会远比这些复杂。


创建数据表

这里,主要创建四个数据表,分别为用户表、商品表、订单表和订单条目表,分别对应着四个实体类,如下所示。

  • t_user用户表,与User实体类对应

图片.png


  • t_product商品表,与Product实体类对应

图片.png


  • t_order订单表,与Order实体类对应

图片.png


  • t_order_item订单条目表,与OrderItem实体类对应

图片.png


创建数据表的脚本已经放在项目源码里啦。至此,项目中的通用工具类模块、通用实体类模块就开发完成了,同时,数据表也创建完毕了。下一篇,我们开撸用户微服务、商品微服务和订单微服务。

小伙们如需获取《SpringCloud Alibaba实战》专栏整体项目的源代码,可以加入【冰河技术】知识星球,至于知识星球嘛,继续往下看。

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
SpringCloudAlibaba Java 网络架构
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(二)Rest微服务工程搭建
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(二)Rest微服务工程搭建
52 0
|
1月前
|
Java Nacos Sentinel
Spring Cloud Alibaba 面试题及答案整理,最新面试题
Spring Cloud Alibaba 面试题及答案整理,最新面试题
208 0
|
1月前
|
SpringCloudAlibaba Java 持续交付
【构建一套Spring Cloud项目的大概步骤】&【Springcloud Alibaba微服务分布式架构学习资料】
【构建一套Spring Cloud项目的大概步骤】&【Springcloud Alibaba微服务分布式架构学习资料】
155 0
|
1月前
|
SpringCloudAlibaba Java 网络架构
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(七)Spring Cloud Gateway服务网关
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(七)Spring Cloud Gateway服务网关
101 0
|
2天前
|
Java API Nacos
第十二章 Spring Cloud Alibaba Sentinel
第十二章 Spring Cloud Alibaba Sentinel
12 0
|
2天前
|
Java Nacos 开发者
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
|
9天前
|
Java API 对象存储
对象存储OSS产品常见问题之使用Spring Cloud Alibaba情况下文档添加水印如何解决
对象存储OSS是基于互联网的数据存储服务模式,让用户可以安全、可靠地存储大量非结构化数据,如图片、音频、视频、文档等任意类型文件,并通过简单的基于HTTP/HTTPS协议的RESTful API接口进行访问和管理。本帖梳理了用户在实际使用中可能遇到的各种常见问题,涵盖了基础操作、性能优化、安全设置、费用管理、数据备份与恢复、跨区域同步、API接口调用等多个方面。
26 2
|
18天前
|
SpringCloudAlibaba 监控 Java
SpringCloud Alibaba微服务-- Sentinel的使用(保姆级)
SpringCloud Alibaba微服务-- Sentinel的使用(保姆级)
|
18天前
|
SpringCloudAlibaba Java API
SpringCloud Alibaba微服务工程搭建(保姆级)
SpringCloud Alibaba微服务工程搭建(保姆级)
|
18天前
|
SpringCloudAlibaba Java Nacos
SpringCloud Alibaba微服务 -- Nacos使用以及注册中心和配置中心的应用(保姆级)
SpringCloud Alibaba微服务 -- Nacos使用以及注册中心和配置中心的应用(保姆级)