说明:当开发中,如交易、文件传输过程中的文件名,可能需要我们使用一串唯一的数字来锁定这一条“交互记录”,即流水号。
本文介绍几种生成6位递增唯一,且每日重置的流水号的方式。
我们可以将上次生成流水号的日期,以及生成的流水号存入到Redis中,需要生成流水号时去Redis中将这两个值取出来做判断。
如果日期不是今天,则从1开始,重新生成;
如果日期是今天,则将流水号的值递增1,返回;
代码如下:
(generateSerialNumber(),生成流水号方法)
private static final String REDIS_KEY_PREFIX = "serialNumber:";
private static final String LAST_GENERATED_DATE_KEY = REDIS_KEY_PREFIX + "lastGeneratedDate";
private static final String SERIAL_NUMBER_KEY = REDIS_KEY_PREFIX + "now";
public synchronized String generateSerialNumber() {
// 获取当前日期
String today = LocalDate.now().toString();
// 如果Redis中没有日期数据,或者Redis中的日期和当前日期不一致
if (!today.equals(redisTemplate.opsForValue().get(LAST_GENERATED_DATE_KEY))) {
redisTemplate.opsForValue().set(LAST_GENERATED_DATE_KEY, today);
redisTemplate.opsForValue().set(SERIAL_NUMBER_KEY, "0");
}
// 流水号自增1,并格式化后返回
long serialNumber = redisTemplate.opsForValue().increment(SERIAL_NUMBER_KEY,1);
return String.format("%06d", serialNumber);
}
(演示)
public void generate(){
for (int i = 0; i < 10; i++) {
System.out.println(generateSerialNumber());
}
}
(只要是同一天,流水号就递增1返回,不是同一天则重置,从1重新开始)
可在Redis中看到这两个值,即最后一次生成流水号的时间,以及生成的流水号
我们可以在数据库里维护一张表(rb_generate_serial_number),这张表里面的数据是最近一次生成的流水号时间(generate_date)以及流水号(last_number)。相当于把存到Redis中的那两个值放到数据库表中存储。如下:
然后写一个存储过程,当在DAO层去调用存储过程时,就生成一个新的流水号。存储过程如下:
CREATE DEFINER=`root`@`localhost` PROCEDURE `rb_generate_serial_number`()
BEGIN
DECLARE currentSerialNumber INT;
DECLARE lastCreateDate DATE;
-- 从表中获取上次生成的流水号和生成的日期
SELECT last_number, generate_date INTO currentSerialNumber, lastCreateDate FROM o_serial_number_log;
-- 检查流水号是否为NULL,如果为NULL,则将其重置为1
IF currentSerialNumber IS NULL THEN
SET currentSerialNumber = 0;
END IF;
-- 检查日期是否为NULL,如果为NULL,则重置流水号为1并更新生成流水号的时间为今天
IF lastCreateDate IS NULL THEN
SET currentSerialNumber = 0;
SET lastCreateDate = CURDATE();
END IF;
-- 检查生成流水号的日期是否为今天
IF lastCreateDate = CURDATE() THEN
-- 如果日期是今天,则递增流水号
SET currentSerialNumber = currentSerialNumber + 1;
ELSE
-- 如果日期不是今天,则重置流水号为1并更新生成流水号的时间为今天
SET currentSerialNumber = 1;
SET lastCreateDate = CURDATE();
END IF;
-- 更新数据库中的流水号和日期
UPDATE o_serial_number_log SET last_number = currentSerialNumber, generate_date = lastCreateDate;
-- 返回6位流水号
SELECT LPAD(currentSerialNumber, 6, '0') AS o_serial_number_log;
END
在代码中写一个Mapper方法,xml里面直接调用这个存储过程,如下:
<select id="generateSerialNumber" resultType="java.lang.String">
call rb_generate_serial_number()
select>
在浏览器中测试,访问该接口,返回流水号的值。因为数据库里面记录的时间不是当天,则从1开始。
数据库表同时更新;
如果你的业务表中,已包含以上两个字段,则可以在生成流水号时,去查询业务表中最新的流水号,然后递增1作为新生成的流水号。
如果日期不是当天,则重置从1开始。
生成唯一递增,且每日重置的流水号,关键就在于将上次生成流水号的信息存储出来,本次生成时再取出来做简单的判断。据此分出三种方式:
存在Redis中,需要考虑Redis宕机的情况;
存在数据库表中,需要额外写存储过程;
从现有的数据库表中获取,需要在业务代码里写判断;