手写个递增号码生成器

一 业务需求

每家船公司的文件序列号从00001开始,最大长度五位数字,超过99999就重置为00001

二 分析

提到流水号,我们可能会想到数据库序列,但是这里要求每一个船公司都要求有一个流水号,如果是每一个船公司都创建一个序列,显然是不正确的,另外,序列其实是从1开始而不是00001,那如何实现这个呢,用字符串拼接可以,但是想起来就是需要一些if else,较为麻烦。 最终选择了方案时,创建一个数据库表,每一个船公司都对应一条数据,且每一个船公司都记录他的流水号,这样就能实现流水号的处理。 另外选择Strings.padStart(seqNo, length, '0');方法对流水号进行补全

三 实现

  1. 新建数据库表CNF_NUMBER

    CREATE TABLE "CNF_NUMBER" 
    (  
      "ID" VARCHAR2(32 CHAR), 
      "CREATED_BY" VARCHAR2(128 CHAR), 
      "CREATED_DATE" TIMESTAMP (6), 
      "LAST_MODIFIED_BY" VARCHAR2(128 CHAR), 
      "LAST_MODIFIED_DATE" TIMESTAMP (6), 
      "VERSION" NUMBER(19,0), 
      "CODE" VARCHAR2(128 CHAR), 
      "MIN_VALUE" NUMBER(19,0), 
      "MAX_VALUE" NUMBER(19,0), 
      "INCREMENT_BY" NUMBER(19,0), 
      "CURRENT_VALUE" NUMBER(19,0), 
       CONSTRAINT "CNF_NUMBER_PK" PRIMARY KEY ("ID")
    );
    
     COMMENT ON TABLE "CNF_NUMBER"IS '自增流水表';
    
     COMMENT ON COLUMN "CNF_NUMBER"."ID" IS '主键';
    
     COMMENT ON COLUMN "CNF_NUMBER"."CREATED_BY" IS '创建人';
    
     COMMENT ON COLUMN "CNF_NUMBER"."CREATED_DATE" IS '创建时间';
    
     COMMENT ON COLUMN "CNF_NUMBER"."LAST_MODIFIED_BY" IS '最后修改人';
    
     COMMENT ON COLUMN "CNF_NUMBER"."LAST_MODIFIED_DATE" IS '最后修改时间';
    
     COMMENT ON COLUMN "CNF_NUMBER"."VERSION" IS '数据行版本,用于乐观锁控制。';
    
     COMMENT ON COLUMN "CNF_NUMBER"."CODE" IS '序列代码';
    
     COMMENT ON COLUMN "CNF_NUMBER"."MIN_VALUE" IS '最小值';
    
     COMMENT ON COLUMN "CNF_NUMBER"."MAX_VALUE" IS '最大值';
    
     COMMENT ON COLUMN "CNF_NUMBER"."INCREMENT_BY" IS '步长';
    
     COMMENT ON COLUMN "CNF_NUMBER"."CURRENT_VALUE" IS '当前值';
    复制代码
  2. 创建递增业务类及逻辑实现

    /**
     * 递增号码生成器
     *
     * @author 子羽
     */
    @Service
    public class NumberService {
    
        @Autowired
        private NumberRepository numberRepository;
    
    
        /**
         * 从指定的序列中获取下一个值。
         */
        @Transactional(propagation = Propagation.REQUIRES_NEW)
        public long countNextValueFrom(String code) {
            return countMessageNextNumberFrom(code).getCurrentValue();
    
        }
    复制代码
    /**
     * 从指定的序列中获取下一个值。
     */
    private synchronized Number countMessageNextNumberFrom(String code) {
        Number num = this.numberRepository.findByCode(code);
        if (num == null) {
            num = Number.newInstance();
            num.setCode(code);
            num.setMaxValue(99999L);
        }
    
        // 循环直到获取到正确的值,避免并发环境下无法正确获取号码的问题
        while (true) {
            try {
                num = this.numberRepository.saveAndFlush(num.incrementAndGet());
                break;
            } catch (OptimisticLockException ole) {
                num = this.numberRepository.findOne(num.getId());
                continue;
            }
        }
        return num;
    
    }
    复制代码

    这里要注意的是,并发时出现乐观锁异常num取当前的流水号

  3. 达到最大值回归最小值的逻辑处理

      public Number incrementAndGet() {
          if (this.currentValue == this.maxValue) {
              this.currentValue = this.minValue;
          }
    
          this.currentValue += this.incrementBy;
          return this;
      }
    复制代码
  4. 使用Strings.padStart()填充流水号

     private String getSeq(String seqName, int length) {
         String seqNo = String.valueOf(this.numberService.countNextValueFrom(seqName));
         return Strings.padStart(seqNo, length, '0');
         
    
     
     public String createSerialNo(String carrierId) {
         return getSeq(carrierId, 5);
     }
    
    复制代码

5.调用

   ```
    // 获得创公司流水号
    String seqNo = this.createSerialNo(eir.getCarrierId())
    ```


    
复制代码

猜你喜欢

转载自juejin.im/post/7016980156325560356