Spring Java服务重启后自增员工编码重置,如何跨重启持久化自增整数值
问题根因
你当前的自增基数存储在JVM静态变量uniqueIndex中,静态变量的生命周期和JVM进程绑定,服务重启后JVM进程销毁重建,所有静态变量都会回到初始赋值的1000,自然会出现编码重置的问题。同时原有代码还存在线程安全隐患,多并发场景下静态变量非原子自增可能生成重复编码。
可行解决方案
方案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;
- 修改业务代码逻辑:
@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:持久化到本地配置文件
适合无数据库的小型单机服务场景,不需要额外引入中间件。
- 服务启动时读取本地配置文件存储的上一次最大ID作为初始基数
- 每次生成新编码后,把最新的基数写回配置文件持久化
示例代码:
@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
相关产品推荐
相关产品推荐

