在开发中,遇到这样一个需求,在介质资料新增时,需要生成一个介质编号,格式为\"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)
+ String.format(format, getIncrement(redisTemplate, code + 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);
}
}
来源:https://www.cnblogs.com/cyb-start/p/16254900.html
本站部分图文来源于网络,如有侵权请联系删除。