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

该文章介绍了一个使用Redis和RedisTemplate实现的自增ID生成类,通过RedisAtomicLong确保原子性,并结合Redis分布式锁和数据库同步,防止并发问题。同时,服务会根据日期重置编号,确保每月编号的唯一性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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"));
    }
}
03-13
<think>好的,用户想了解自运算符的概念和用法,包括使用方法及示例。我需要结合提供的引用资料来组织回答。首先,先明确自运算符的基本定义,分为前置和后置两种形式,如++i和i++。 根据引用[2]和[5],C++中前置和后置运算符的返回不同,前置返回自后的左,后置返回自前的右。这点需要重点说明,并且提到重载时的区别,比如引用[4]提到的占位参数int用于区分后置形式。 然后,使用方法部分需要分别介绍前置和后置,给出示例代码。例如,前置自先加1再使用,后置则先使用再加1。示例可以用简单的变量自,比如int a = 5; a++和++a的结果差异。 此外,引用[3]提到自不仅适用于整数,还支持其他类型,比如浮点数和字符,可能需要举例说明,比如字符‘A’自后变为‘B’。 关于运算符重载,引用[4]和[2]指出在C++中如何重载前置和后置,特别是后置需要一个int参数来区分。这里可以给出一个简单的类示例,比如自定义的Integer类,重载++运算符,展示成员函数的写法。 注意事项部分,需要提到避免在复杂表达式中多次使用同一变量的自运算符,以免引起未定义行为,例如i = i++这样的表达式结果不确定。 最后,生成相关问题,比如行为差异、重载区别、非整型应用等。确保回答结构清晰,分点说明,并正确引用资料。</think>### 自运算符的用法及示例 自运算符(`++`)用于将变量的加1,分为**前置自**(如 `++x`)和**后置自**(如 `x++`)。以下是详细说明: --- #### 1. **基本概念** - **前置自**(`++x`):先对变量加1,再使用。例如: ```cpp int a = 5; int b = ++a; // a=6, b=6 ``` - **后置自**(`x++`):先使用,再对变量加1。例如: ```cpp int a = 5; int b = a++; // a=6, b=5 ``` --- #### 2. **返回区别** - **前置自**返回自后的**左**(内存地址,可被赋)[^5]。例如: ```cpp int x = 3; ++x = 5; // 合法,x=5 ``` - **后置自**返回自前的**右**(临时,不可被赋)。例如: ```cpp int x = 3; x++ = 5; // 非法,编译错误 ``` --- #### 3. **应用示例** - **简单数操作**: ```cpp int i = 0; cout << i++ << endl; // 输出0,i变为1 cout << ++i << endl; // 输出2,i变为2 ``` - **循环中的典型用法**: ```cpp for (int i = 0; i < 5; ++i) { cout << i << " "; // 输出0 1 2 3 4 } ``` --- #### 4. **支持的数据类型** 自运算符不仅适用于整数,也支持: - **浮点数**(如 `double x = 3.5; x++;`,结果为4.5)[^3]。 - **字符**(如 `char c = 'A'; c++;`,结果为 `'B'`)。 --- #### 5. **运算符重载(C++)** 在自定义类中重载自运算符时: - **前置自**定义为成员函数: ```cpp class Integer { int value; public: Integer& operator++() { // 前置++ ++value; return *this; } }; ``` - **后置自**通过添加 `int` 占位参数区分: ```cpp class Integer { int value; public: Integer operator++(int) { // 后置++ Integer temp = *this; ++value; return temp; // 返回旧 } }; ``` 后置运算符返回旧的临时副本,因此效率通常低于前置自[^4][^2]。 --- #### 6. **注意事项** - **避免未定义行为**:如 `int i = 0; i = i++;` 的结果不确定。 - **语义清晰性**:在复杂表达式中减少混合使用前置/后置自。 --- §§ 1. 前置自与后置自在循环中的性能差异是什么? 2. 如何在Python中模拟自运算符的行为? 3. 自运算符是否适用于字符串或其他非数类型?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

hikktn

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

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

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

打赏作者

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

抵扣说明:

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

余额充值