使用MySQL生成
相信大家在开发上都会遇到业务单号生成的需求,一般的生成格式为:
代码语言:javascript复制前缀 YYMMDD 序列号(例如3位序列号)
这里前缀 日期没有太大技术含量,主要是在尾部的序列号,这里要求3位序列号,我们从001开始。当等于10起,为010。当等于100起,为100。第二天重置从001起。这里我们可以使用MySQL
数据库存储,这里我们需要记录日期和序号两个字段。MySQL
表如下:
CREATE TABLE `code_generator` (
`id` bigint(10) NOT NULL AUTO_INCREMENT COMMENT 'id',
`number` bigint(10) DEFAULT NULL COMMENT '序号',
`gnerator_time` datetime DEFAULT NULL COMMENT '生成日期',
`deleted` tinyint(1) DEFAULT '0' COMMENT '是否删除',
`created_by` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建人',
`created_time` datetime DEFAULT NULL COMMENT '创建时间',
`updated_by` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '更新人',
`updated_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='单号生成器'
调用生成方法时,判断当前日期与数据库生成日期gnerator_time
是否为同一天。如果为同一天,在当天的数据下面 number 1
。如果不为同一天,删掉当前数据,插入一条number
为1的数据。这里在计算number
时注意添加锁,防止多线程下计算不准确。
使用Redis生成
当然,除了使用MySQL生成,还可以使用Redis
来生成。相较于MySQL,Redis
的性能更为优良。
这里我们使用Redisson
API。
首先我们使用Spring注入一个Redisson
客户端:
@Bean("redissonService")
public RedissonClient getRedissonClient() {
String activeProfile = getActiveProfile();
Config config;
if ("prod".equals(activeProfile)) {
config = useClusterConfig();
} else {
config = useSingleConfig();
}
return Redisson.create(config);
}
在配置RedissonClient
时,可以设置为单机模式,或者集群模式。
这样就可以从容器中获取bean了。
代码语言:javascript复制@Autowired
private RedissonClientConfig redissonService;
生成编号的核心代码如下:
代码语言:javascript复制private synchronized long getSuffixCode(String key) {
RedissonClient redissonClient = redissonService.getRedissonClient();
RAtomicLong atomicVar = redissonClient.getAtomicLong(key);
String todayStr = getTodayStr();
String codeRecord = getCodeRecord(key);
if (!atomicVar.isExists()) {
atomicVar.set(1);
}
if (StringUtils.isNotBlank(codeRecord)) {
if (!isSameDay(todayStr, codeRecord)) {
atomicVar.set(1);
}
}
saveCodeRecord(key, todayStr);
long value = atomicVar.incrementAndGet();
return value;
}
上面代码功能为生成序号。我们先在redis
中查找,如果通过key能查到对应的值,则在这个值的基础上增加1。如果值不存在,设置为1。当然,如果当天日期与redis
记录日期不是同一天,也需要将值置为1。
saveCodeRecord方法代码如下:
代码语言:javascript复制private void saveCodeRecord(String key, String value) {
String recordKey = key CODE_DATE_RECORD_KEY;
redisUtils.set(recordKey, value);
序号如果小于10,则前面补两个个0。在10到100之间,补两个0。序号补位方法如下:
代码语言:javascript复制private String getSuffixCodeStr(String key, int digit) {
long code = getSuffixCode(key);
return getDigitNumber((int) code, digit);
}
public String getDigitNumber(int number, int digit) {
NumberFormat formatter = NumberFormat.getNumberInstance();
formatter.setMinimumIntegerDigits(digit);
formatter.setGroupingUsed(false);
return formatter.format(number);
}
之后我们再将前缀,日期,序号拼接起来即可:
代码语言:javascript复制private String generateCode(String prefix, String redisKey, int digit) {
String dateStr = getTodayStr();
String suffixCodeStr = getSuffixCodeStr(redisKey, digit);
String[] codes = {prefix, dateStr, suffixCodeStr};
return String.join("", codes);
}
我们再做一层简单的封装:
代码语言:javascript复制public String generatorCode(String prefix, int digit) {
String redisKey = getRedisKey(prefix, "GENERAL_CODE");
return generateCode(prefix, redisKey, digit);
}
对generatorCode()
说明如下:
❝生成的单号格式为:前缀 YYMMDD 序号 例如:
generatorCode("D",4
),当前日期为:2022-08-12 则生成单号:D202208120001 ❞