随笔 - 2  文章 - 0  评论 - 1  阅读 - 4463

Java中流水编号的生成

在开发中,遇到这样一个需求,在介质资料新增时,需要生成一个介质编号,格式为"JZ+yyyyMMdd+4位递增数字"
先是使用百度找寻解决方法。解决方法

里面的查询缓存的方法在我这项目里没有,我也不会写,就自己想了个折中的方法。在请求这个接口的时候,先去数据库查询MAX(id),如果有,就在此基础上+1
如果没有,就初始化一个值1进行传参。相关代码如下:

点击查看代码
public class NumberUtils {
/**
* 生成14位唯一流水号,"JZ"+yyyyMMdd+4位数字
* 4位数字,如:0001
* @return
*/
@NotNull
public static String generateSerialNum(String str, Integer nowNum){
// 定义需要返回的流水号
String serialNum;
// 先查询到今天的日期,格式:"yyyyMMdd"
String todayDate = new SimpleDateFormat("yyyyMMdd")
.format(new Date());
// 固定字母前缀 拼接 今天日期,组成新的完整的前缀
String prefixCode = str + todayDate;
serialNum = getCode(prefixCode, nowNum);
return serialNum;
}
/**
* 将数值拼接成对应的位数
* @param prefix 前缀:"JZ"+yyyyMMdd
* @param nowNum 当前要生成的数字
* @return 拼接好的流水号
*/
private static String getCode(String prefix,int nowNum ) {
// 需要返回的code
StringBuilder code = new StringBuilder();
// 需要拼接的数字
StringBuilder num = new StringBuilder();
// 封装的数字对象,里面 value 加了 volatile关键字,保证了线程安全
AtomicInteger count = new AtomicInteger(nowNum);
// 将数值补足为4位字符串
if (count.get() < 10) {
num.append("000").append(count.get());
} else if(count.get() < 100){
num.append("00").append(count.get());
}else if(count.get() < 1000){
num.append("0").append(count.get());
} else if (count.get() >= 1000) {
num.append(count.get());
}
// 先拼接前缀
code.append(prefix);
// 再拼接数字
code.append(num);
return code.toString();
}
}

后面被说不行,并发下不行。而且跟数据库连接效率也低。就想到了使用redis的incr方法。相关代码如下:

点击查看代码
public class NumberUtils {
/**
* 编号自动生成
* @param redisTemplate redis模板
* @param code 前缀 首字母大写
* @param dateFormat 日期格式
* @param nums 几位数字
* @return 编号
*/
@NotNull
public static String generateSerialNum(@NotNull RedisTemplate<String,Integer> redisTemplate,
String code, String dateFormat, Integer nums) {
// 使用StringBuilder拼接字符串
StringBuilder sb = new StringBuilder();
// 获取当前时间并定义日期格式
String localDateFormat = DateUtils.localDateFormat(LocalDate.now(), dateFormat);
sb.append(code);
sb.append(localDateFormat);
// 将拼接好的字符串做key,获取redis中的数据
Integer i = redisTemplate.opsForValue().get(sb.toString());
int increment;
if (i != null) {
// 如果redis中有数据,则使用increment()将数据+1
increment = Objects.requireNonNull(redisTemplate.opsForValue()
.increment(sb.toString())).intValue();
} else {
// 如果redis中没有数据,则使用setIfAbsent()将数据设置为1
increment = 1;
redisTemplate.opsForValue().setIfAbsent(sb.toString(),increment);
}
// 第一次format格式化nums位0,之后再格式化increment
sb.append(String.format(String.format("%%0%dd", nums), increment));
return sb.toString();
}
}

然后说代码逻辑没问题,就是代码可以再优化,一通解释加我一通操作,最后代码如下:

点击查看代码
@Slf4j
public class NumberUtils {
private NumberUtils() {}
/**
* 编号自动生成
*
* @param redisTemplate redis模板
* @param code 前缀 首字母大写
* @param dateFormat 日期格式
* @param nums 几位数字
* @return 编号
*/
@NotNull
public static String generateSerialNum(RedisTemplate<String,Integer> redisTemplate,
String code, String dateFormat, Integer nums) {
// nums位数字,不够的前面补0
val format = String.format("%%0%dd", nums);
// 返回拼接好的编号
return code + DateUtils.localDateFormat(LocalDate.now(), dateFormat)
// redisKey组成:前缀+格式化后的具体日期,确保每天都是新的key,编号从0001开始
+ String.format(format, getIncrement(redisTemplate, code + DateUtils.localDateFormat(LocalDate.now(), dateFormat)));
}
private static Integer getIncrement(@NotNull RedisTemplate<String,Integer> redisTemplate, String redisKey) {
return Optional.ofNullable(redisTemplate.opsForValue().get(redisKey))
// 返回redis里的值 + 1
.map(num -> Objects.requireNonNull(redisTemplate.opsForValue().increment(redisKey)).intValue())
// 如果redis里没有,则插入1并通过判断插入是否成功来返回初始化值1,如果插入失败,则返回拼接null
.orElse(Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(redisKey, 1)) ? 1 : null);
}
}
posted on   长夜余火  阅读(4405)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示