一 业务需求
每家船公司的文件序列号从00001开始,最大长度五位数字,超过99999就重置为00001
二 分析
提到流水号,我们可能会想到数据库序列,但是这里要求每一个船公司都要求有一个流水号,如果是每一个船公司都创建一个序列,显然是不正确的,另外,序列其实是从1开始而不是00001,那如何实现这个呢,用字符串拼接可以,但是想起来就是需要一些if else,较为麻烦。 最终选择了方案时,创建一个数据库表,每一个船公司都对应一条数据,且每一个船公司都记录他的流水号,这样就能实现流水号的处理。 另外选择Strings.padStart(seqNo, length, '0');
方法对流水号进行补全
三 实现
-
新建数据库表
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 '当前值'; 复制代码
-
创建递增业务类及逻辑实现
/** * 递增号码生成器 * * @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取当前的流水号
-
达到最大值回归最小值的逻辑处理
public Number incrementAndGet() { if (this.currentValue == this.maxValue) { this.currentValue = this.minValue; } this.currentValue += this.incrementBy; return this; } 复制代码
-
使用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())
```
复制代码