最近开发时,又遇到一个需求,后端需要生成自增的流水号,流水号格式一般就是:标志位 + 年月日时分秒 + 一定位数的流水号 组成,例如: T20190809135743000001 。
根据先前的查询和总结实现,现在对该需求的解决方法做一个整理记录。
先前在网上看各位大佬对自增流水号的处理,一般分成三种:
1. 通过在Java类中生成;
2.依托数据库自增函数生成;
3.依托redis自增生成
因为我当前项目中本身就整合redis,而且redis是单线程,且基于内存操作,速度快,实现自增流水号代码也简单,所以我选用的是第三种。
实现自增流水号,格式 标志位 + 年月日时分秒 + 一定位数的自增流水号 ,首先把这个分成两部分,一部分是前面的标志位 + 年月日时分秒,另一部分是一定位数的自增流水号,分别实现,最后拼接输出即可。
第一步:
生成流水号第一部分:标志位 + 年月日时分秒,这个很简单,不多说,代码如下:
StringBuffer sbuffer = new StringBuffer();
sbuffer.append("T"); //标志位
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
sbuffer.append(sdf.format(new Date())); //年月日时分秒
第二步,调用redis的自增函数,获得流水号第二部分,自增
//设置6自增6位,用于补全操作
private static final String STR_FORMAT = "000000";
/**
* redis流水号自增
* @param key 自己设置,保存当前自增值
* @param liveTime 在redis中的缓存时间,方法中设置单位(秒/分/天……)
* @return
*/
public String incr(String key, long liveTime) {
RedisAtomicLong entityIdCounter = new RedisAtomicLong(key, stringRedisTemplate.getConnectionFactory());
Long increment = entityIdCounter.getAndIncrement();
if ((null == increment || increment.longValue() == 0) && liveTime > 0) {//初始设置过期时间
entityIdCounter.expire(liveTime, TimeUnit.DAYS); //设置自增值过期时间,liveTime 过期时间;TimeUnit.DAYS 过期时间单位,我这边设置为天
}
if (increment == 0) {
increment = increment + 1;
} else if (increment > 999999){
increment = 1L;
}
//位数不够,前面补0
DecimalFormat df = new DecimalFormat(STR_FORMAT);
return df.format(increment);
}
上述方法中,需要注意:
1.为保证每一天的流水号都是从01开始,所以我们的自增流水号对应的key必须要带上当天日期,例如:T20190809
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
String incKey = "T" + dateFormat.format(new Date());
2. 设置自增值的过期时间,即什么时候让redis销毁无用的保存自增值的key
3.保证流水号输出的位数不变,需要对自增值做位数补0操作
4.当前流水号大于设置的位数时,需要从头开始计数。
只要把上述两个代码片段结果拼接在一起,就能得到我们需要的自增流水号:T20190809135743000001
@Override
public String selectTaskNo() {
//格式:T+yyyymmddHHmiss+6位流水
StringBuffer sbuffer = new StringBuffer();
sbuffer.append("T");
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
sbuffer.append(sdf.format(new Date()));
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
String incKey = "T" + dateFormat.format(new Date());
String no = incr(incKey, 1);
sbuffer.append(no);
System.out.println(">>>>>>>>>>>" + sbuffer.toString());
return sbuffer.toString();
}
用 postMan 并发测试当前接口(并发值:100),控制台输出正确值,没有出现乱序现象