自定义规则生成唯一ID(方案一:通过SQL生成)
- 场景:
生成物流唯一ID,要求根据创建的订单时间及该订单所属类型,举例:
有一个订单类型为‘普货’,该订单时间为‘2022-07-19 00:00:00’,且该订单为7月底第一订单,则物流ID为:P-2207000001,后面同一类型同一个月的物流ID自增,P-2207000002,规则:类型-时间-指定位数数字字符(type-yyMM-00000...)
- 实战:
现有类型:集团外整车(J-)、普货(P-)、零部件(L-)
规则:类型yyMM六位自增数字
实现:
通过SQL直接获取下一个物流唯一ID
SELECT (CASE
WHEN ID_GEN IS NULl AND '零部件'= #{bigType} THEN 'L-'||TO_CHAR(#{orderDate}, 'yyMM')||'000001'
WHEN ID_GEN IS NULl AND '集团外整车'= #{bigType} THEN 'J-'||TO_CHAR(#{orderDate}, 'yyMM')||'000001'
WHEN ID_GEN IS NULl AND '普货'= #{bigType} THEN 'P-'||TO_CHAR(#{orderDate}, 'yyMM')||'000001'
WHEN ID_GEN IS NOT NULl AND '零部件'= #{bigType} THEN 'L-'|| TO_CHAR(#{orderDate}, 'yyMM')|| ID_GEN
WHEN ID_GEN IS NOT NULl AND '集团外整车'= #{bigType} THEN 'J-' || TO_CHAR(#{orderDate}, 'yyMM')||ID_GEN
WHEN ID_GEN IS NOT NULl AND '普货'= #{bigType} THEN 'P-'|| TO_CHAR(#{orderDate}, 'yyMM')|| ID_GEN
ELSE NULL END) AS wayWillNo
FROM (
SELECT TO_CHAR(TO_NUMBER(MAX(regexp_replace(WAYBILL_NO, '[^0-9]')))+1, 'fm000000') AS ID_GEN FROM OB_ORDER WHERE BIG_TYPE=#{bigType} AND TO_CHAR(DTORDER_DATE, 'yyyyMM')=TO_CHAR(#{orderDate}, 'yyyyMM')
)
</select>
考虑使用的并发问题,导致取到生成相同的ID,获取ID通过该方法获取,加锁,并预生成部分唯一ID
@Service
public class SeqService {
private final ReentrantLock lock = new ReentrantLock();
private static final Map<String,List<String>> waybillMap=new ConcurrentHashMap<>();
@Autowired
private OrderMapper orderMapper;
public String genWaybillNo(String bigType, Date dtOrderDate){
lock.lock();
try {
List<String> list = waybillMap.get(bigType + DateUtils.format(dtOrderDate, "yyMM"));
if (CollectionUtil.isEmpty(list)) {
if(list==null){
list=new ArrayList<>();
}
String waybillNo = orderMapper.genWaybillNo(bigType, dtOrderDate);
String pre = ("集团外整车".equals(bigType) ? "J-"
: ("普货".equals(bigType) ? "P-" : "L-") )+ DateUtils.format(dtOrderDate, "yyMM");
for (int i = 0; i < 9; i++) {
waybillNo = getLastWayBillNo(pre, waybillNo);
list.add(waybillNo);
}
return waybillNo;
} else {
String waybillNo = list.stream().findFirst().get();
list.remove(waybillNo);
waybillMap.put(bigType + DateUtils.format(dtOrderDate, "yyMM"), list);
return waybillNo;
}
}finally {
System.out.println("generate seq unlock");
lock.unlock();
}
}
//生成下一个
private static String getLastWayBillNo(String pre,String waybillNo){
String number = StrUtil.removePrefix(waybillNo, pre);
if (!StringUtils.isEmpty(number)) {
int n = number.length(); //取出字符串的长度
int num = Integer.parseInt(number) + 1; //将该数字加一
String added = String.valueOf(num);
n = Math.min(n, added.length());
//拼接字符串
return waybillNo.subSequence(0, waybillNo.length() - n) + added;
} else {
throw new NumberFormatException();
}
}
}
效果:
自定义规则生成唯一ID(方案二:通过redis生成)
- 将固定前缀+类型+yyMM作为key,获取ID时,如果为空,拼接生成第一个ID,并set到redis,如果不为空,将取到的值,生成下一个ID并set的redis,生成方法请参考方案一。
- 或者reids只存数字 incr,前缀自己拼接