You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Java服务重启后自增员工编码重置,如何跨重启持久化自增整数值

问题根因

你当前的自增基数存储在JVM静态变量uniqueIndex中,静态变量的生命周期和JVM进程绑定,服务重启后JVM进程销毁重建,所有静态变量都会回到初始赋值的1000,自然会出现编码重置的问题。同时原有代码还存在线程安全隐患,多并发场景下静态变量非原子自增可能生成重复编码。


可行解决方案

方案1:使用数据库自增序列(最推荐,生产环境常用)

适用于已接入数据库存储员工数据的场景,利用数据库的持久化特性保证自增序列不会丢失,一致性最高。

  1. 先在数据库创建序列表(也可以直接复用员工表的自增主键),MySQL示例结构如下:
CREATE TABLE emp_code_sequence (
  id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  stub CHAR(1) NOT NULL DEFAULT '',
  UNIQUE KEY stub (stub)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1000;
  1. 修改业务代码逻辑:
@Service
public class EmployeeServiceImpl implements EmployeeService  {
    private static final String PREFIX = "AWL";
    
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Transactional
    public String setEmpCode() {
        // 插入占位数据获取自增ID,重复插入也不会影响序列自增
        jdbcTemplate.update("INSERT INTO emp_code_sequence (stub) VALUES ('a') ON DUPLICATE KEY UPDATE stub = VALUES(stub)");
        Long newId = jdbcTemplate.queryForObject("SELECT 2507080", Long.class);
        return PREFIX + newId;
    }
}

如果员工表本身的主键就是自增属性,也可以直接插入员工数据后拿生成的主键拼接前缀作为编码,不需要额外建表。


方案2:持久化到本地配置文件

适合无数据库的小型单机服务场景,不需要额外引入中间件。

  1. 服务启动时读取本地配置文件存储的上一次最大ID作为初始基数
  2. 每次生成新编码后,把最新的基数写回配置文件持久化
    示例代码:
@Service
public class EmployeeServiceImpl implements EmployeeService  {
    private static final String PREFIX = "AWL";
    // 配置文件存放路径,需要保证服务进程对该路径有读写权限
    private static final String CONFIG_PATH = "/data/emp_code_config.properties";
    private AtomicInteger currentIndex;

    // 服务启动时初始化读取存储的ID值
    @PostConstruct
    public void init() throws IOException {
        Properties prop = new Properties();
        try(FileInputStream fis = new FileInputStream(CONFIG_PATH)) {
            prop.load(fis);
            int initValue = Integer.parseInt(prop.getProperty("last.max.id", "1000"));
            currentIndex = new AtomicInteger(initValue);
        } catch (FileNotFoundException e) {
            // 首次启动文件不存在,默认从1000开始
            currentIndex = new AtomicInteger(1000);
            persistCurrentIndex();
        }
    }

    public synchronized String setEmpCode() throws IOException {
        int newId = currentIndex.getAndIncrement();
        // 生成新ID后同步持久化到本地文件
        persistCurrentIndex();
        return PREFIX + newId;
    }

    private void persistCurrentIndex() throws IOException {
        Properties prop = new Properties();
        prop.setProperty("last.max.id", String.valueOf(currentIndex.get()));
        try(FileOutputStream fos = new FileOutputStream(CONFIG_PATH)) {
            prop.store(fos, "Employee code auto increment config");
        }
    }
}

方案3:用Redis等分布式缓存存储自增基数

适合分布式多实例部署的场景,自增性能比数据库更高,只要开启Redis持久化就不会丢失数据。
示例代码:

@Service
public class EmployeeServiceImpl implements EmployeeService  {
    private static final String PREFIX = "AWL";
    private static final String REDIS_SEQ_KEY = "emp:code:sequence";
    
    @Autowired
    private StringRedisTemplate redisTemplate;

    public String setEmpCode() {
        Long newId = redisTemplate.opsForValue().increment(REDIS_SEQ_KEY);
        // 首次调用时如果是初始值1,重置为起始值1000
        if(newId == 1) {
            redisTemplate.opsForValue().set(REDIS_SEQ_KEY, "1000");
            newId = 1000L;
        }
        return PREFIX + newId;
    }
}

需要提前开启Redis的RDB/AOF持久化配置,避免Redis重启丢失序列数据。

内容的提问来源于stack exchange,提问作者user2715085

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 23:48:03