一 业务需求
每家船公司的文件序列号从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())