SpringBoot 使用redis RedisAtomicLong生成自增的ID值

1、自增ID生成类

RedisSequenceFactory是一个简单封装类,用于使用redisTemplate生成自增ID值。代码如下:

@Slf4j
@Service
public class IdGenerateServiceImpl extends ServiceImpl<IdGenerateMapper, IdGenerate> implements IdGenerateService {
    @Autowired
    RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private RedissonLocker redissonLocker;

    private final Lock lock = new ReentrantLock();

    /**
     * 获取链接工厂
     */
    public RedisConnectionFactory getConnectionFactory() {
        return Objects.requireNonNull(redisTemplate.getConnectionFactory());
    }

    /**
     * 自增数
     *
     * @param key key
     * @return 自增数
     */
    public long increment(String key) {
        RedisAtomicLong redisAtomicLong;
        // 加Redis分布式锁 双重保险,防止初始化的时候并发
        // 【不存在会有两种情况,一种是没有这个key的自增,一种是数据库中有,redis过期了或重启了】
        if (Boolean.FALSE.equals(redisTemplate.hasKey(key))) {
            try {
                lock.lock();
                //查询是否在数据库中存在
                IdGenerate idGenerate = this.getOne(Wrappers.lambdaQuery(IdGenerate.class).eq(IdGenerate::getGenerateKey, key));
                redisAtomicLong = new RedisAtomicLong(key, this.getConnectionFactory());
                if (idGenerate != null) {
                    // 从数据库赋值后,redis初始化不自增
                    redisAtomicLong.set(idGenerate.getCurrentNo());
                }
                // -1 为不过期
                //log.warn(String.valueOf(redisAtomicLong.getExpire()));
            } finally {
                // 解锁
                lock.unlock();
            }
        } else {
            // 如果存在key,increment 是原子性的,不会重复计数
            redisAtomicLong = new RedisAtomicLong(key, this.getConnectionFactory());
            // 如果存在key,根据key的创建月份时间,超过本月的编号,将会重新生成
            String[] keys = key.split(StringConstant.Symbol.COLON);
            // 转换时间类型
            TemporalAccessor yyyyMM = DateTimeFormatter.ofPattern("yyyyMM").parse(keys[1]);
            LocalDate of = LocalDate.of(yyyyMM.get(ChronoField.YEAR), yyyyMM.get(ChronoField.MONTH_OF_YEAR), 1);
            // 获取本月第一天
            LocalDate now = LocalDate.now();
            LocalDate firstDay = now.with(TemporalAdjusters.firstDayOfMonth());
            // 历史的编号key的月份小于当前月份,则重新生成key
            if (of.getMonthValue() < firstDay.getMonthValue()) {
                try {
                    redissonLocker.lock(key);
                    // 删除key
                    redisTemplate.delete(key);
                } finally {
                    // 解锁
                    redissonLocker.unlock(key);
                }
            }
        }
        return redisAtomicLong.incrementAndGet();
    }

    @Override
    public String generateId(int codeLength, String prefix, String rule) {
        SerialNumber serialNumber = SerialNumber.getInstance();
        // 格式:xxx:202204
        String key = prefix + StringConstant.Symbol.COLON + serialNumber.generateKey();
        // 从 redis 中获取一个自增的计数器
        long increment = increment(key);
        // 完整的编码 例如:xxx_202204_001477
        String finalCode = serialNumber.generateForDate(prefix, increment, codeLength);
        // 保存到数据库中
        IdGenerate idGenerate = new IdGenerate();
        idGenerate.setRule(rule);
        idGenerate.setGenerateKey(key);
        idGenerate.setPrefix(prefix);
        idGenerate.setCurrentNo(increment);
        idGenerate.setCodeLength(codeLength);

        this.saveToTable(idGenerate);
        return finalCode;
    }

    /**
     * 保存id生成器
     *
     * @param entity 新增或修改对象
     * @return 保存成功或失败
     */
    @Transactional
    public boolean saveToTable(IdGenerate entity) {
        boolean submit;
        IdGenerate idGenerateValue = this.getOne(Wrappers.lambdaQuery(IdGenerate.class).eq(IdGenerate::getGenerateKey, entity.getGenerateKey()));
        IdGenerate idGenerate = new IdGenerate();
        if (idGenerateValue == null) {
            idGenerate.setRule(entity.getRule());
            idGenerate.setGenerateKey(entity.getGenerateKey());
            idGenerate.setPrefix(entity.getPrefix());
            idGenerate.setCurrentNo(entity.getCurrentNo());
            idGenerate.setCodeLength(entity.getCodeLength());
            idGenerate.setCreateTime(LocalDateTime.now());
            // 不存在,新增
            submit = this.save(idGenerate);
        } else {
            idGenerate.setId(idGenerateValue.getId());
            idGenerate.setCurrentNo(entity.getCurrentNo());
            idGenerate.setUpdateTime(LocalDateTime.now());
            submit = this.updateById(idGenerate);
        }
        return submit;
    }
}
@Mapper
public interface IdGenerateMapper extends BaseMapper<IdGenerate> {
}
@Data
@TableName("id_generate")
public class IdGenerate implements Serializable {

    /**
     * 主键
     */
    @NotNull(message = "[主键]不能为空")
    @ApiModelProperty("主键")
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    /**
     * 编号规则
     */
    @Size(max = 255, message = "编码长度不能超过255")
    @ApiModelProperty("编号规则")
    @Length(max = 255, message = "编码长度不能超过255")
    private String rule;
    /**
     * redis的key
     */
    @Size(max = 120, message = "编码长度不能超过120")
    @ApiModelProperty("redis的key")
    @Length(max = 120, message = "编码长度不能超过120")
    private String generateKey;
    /**
     * 编号前缀
     */
    @Size(max = 50, message = "编码长度不能超过50")
    @ApiModelProperty("编号前缀")
    @Length(max = 50, message = "编码长度不能超过50")
    private String prefix;
    /**
     * 当前编号
     */
    @ApiModelProperty("当前编号")
    private Long currentNo;
    /**
     * 序号长度 默认0填充
     */
    @ApiModelProperty("序号长度 默认0填充")
    private Integer codeLength;
    /**
     * 创建时间
     */
    @ApiModelProperty("创建时间")
    private LocalDateTime createTime;
    /**
     * 修改时间
     */
    @ApiModelProperty("修改时间")
    private LocalDateTime updateTime;

}

编写一个编号生成的规则类

public class SerialNumber {

    private volatile static SerialNumber serialNumber;

    public SerialNumber() {
    }

    public static SerialNumber getInstance() {
        if (serialNumber != null) {
            return serialNumber;
        }
        synchronized (SerialNumber.class) {
            if (serialNumber != null) {
                return serialNumber;
            }
            serialNumber = new SerialNumber();
        }
        return serialNumber;
    }

    /**
     * @param prefix (前缀)
     * @return id
     */
    public String generate(String prefix, Integer increment, Integer length) {
        //** 自增 *//*
        String finalCode;
        String formatCounter = generateId(length, increment);
        // 生成后的计数器
        if (StringUtils.isNotEmpty(prefix)) {
            finalCode = prefix + formatCounter;
        } else {
            finalCode = String.valueOf(increment);
        }
        return finalCode;
    }

    /**
     * 完整格式  xxx_202204_001477
     * @param prefix 前缀 xxx_202204
     * @param increment 自增数
     * @param length 0的补位长度
     * @return 编号
     */
    public String generateForDate(String prefix, long increment, Integer length) {
        //** 自增 *//*
        String finalCode;
        String formatCounter = generateForDateId(length, increment);
        // 生成后的计数器
        if (StringUtils.isNotEmpty(prefix)) {
            finalCode = prefix + formatCounter;
        } else {
            finalCode = String.valueOf(increment);
        }
        return finalCode;
    }

    public String generateId(Integer length, Integer increment) {
        return String.format("%0" + length + "d", increment);
    }

    /**
     * 生成id(每日重置自增序列)
     * 格式:日期 + 6位自增数
     * 如:202108_000001
     *
     * @param length 几位自增数
     * @return id
     */
    public String generateForDateId(Integer length, long increment) {
        return this.generateKey() + StringConstant.Symbol.UDLINE + String.format("%0" + length + "d", increment);
    }

    public String generateKey() {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime utc = DateUtil.toUtc(now);
        return utc.format(DateTimeFormatter.ofPattern("yyyyMM"));
    }
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
使用Redis生成自增ID,可以借助Redis的INCR命令。INCR命令用于将一个键的增加1,并返回增加后的。你可以在你的代码中使用INCR命令来生成自增ID。下面是一个示例代码片段: ``` @ApiOperation("根据key生成自增Id") @RequestMapping(value = "/getId/{key}", method = RequestMethod.GET) public GenericResponse<Long> getId(@PathVariable("key") String key) { logger.info("生成Id中"); Long id = redisTemplate.opsForValue().increment(key, 1); return new GenericResponse<>(id); } ``` 在这个示例中,我们使用Spring Data Redis提供的`redisTemplate`来执行INCR命令,通过操作键的来实现自增。当调用这个接口时,每次都会生成一个自增ID,并返回给用户。你可以根据实际需求对生成ID进行格式化,比如添加日期前缀或补0等操作。 需要注意的是,为了使用Redis生成自增ID,你需要先确保已经正确地配置了Redis服务器,并在代码中引入相关的依赖。此外,你还可以使用Redis的其他功能,比如设置过期时间等,来满足自己的业务需求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [REDIS生成分布式环境下自增ID](https://blog.csdn.net/yucaifu1989/article/details/122132257)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [毕设项目:基于SpringBoot+MyBatis-Plus 前后端分离的影院选座购票系统.zip](https://download.csdn.net/download/dd_vision/88222307)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hikktn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值