Spring Boot 2转3:Hibernate旧HiLo生成器迁移@TableGenerator初始ID不一致
解决Spring Boot 3迁移后@TableGenerator与旧MultipleHiLoPerTableGenerator的ID序列匹配问题
问题核心差异
旧MultipleHiLoPerTableGenerator与新@TableGenerator的核心逻辑冲突点:
- 旧生成器:读取数据库
seq_value后,先使用[当前值, 当前值+max_lo]区间(如seq_value=0时生成0-49),使用完区间后再将seq_value递增为50 - 新生成器(含
legacy-hilo优化器):先将数据库seq_value递增,再使用[新值, 新值+allocationSize-1]区间(如原seq_value=0时,先增到50再生成50-99,或因初始值逻辑生成1-50)
这种读写顺序+区间偏移的差异,导致多应用共享Oracle库时出现ID重复。
解决方案
方案1:自定义Hibernate ID优化器(精准匹配旧逻辑)
通过自定义优化器完全复刻旧生成器的"先使用、后递增"逻辑:
public class LegacyHiLoOptimizer extends AbstractOptimizer { private long lastValue; private long hi; private long lo; private final long maxLo; public LegacyHiLoOptimizer(Class<?> returnClass, int incrementSize) { super(returnClass, incrementSize); this.maxLo = incrementSize - 1; // allocationSize=50对应maxLo=49,对齐旧逻辑 } @Override public synchronized Serializable generate(AccessCallback callback) { if (lo > maxLo) { // 耗尽当前区间时,读取数据库值并做偏移,模拟旧逻辑的"先用后增" hi = callback.getNextValue() - 1; lo = 0; } lastValue = hi * (maxLo + 1) + lo++; return convert(lastValue); } @Override public long getLastValue() { return lastValue; } @Override public boolean applyIncrementSizeToSourceValues() { return false; } }
配置使用自定义优化器:
spring.jpa.properties.hibernate.id.optimizer.pooled.preferred=com.yourpackage.LegacyHiLoOptimizer
保持@TableGenerator参数与旧结构一致:
@TableGenerator(name = "tk_sequence", table = "hibernate_seq", pkColumnName = "seq_name", pkColumnValue = "seq_token", valueColumnName = "seq_value", allocationSize = 50, initialValue = 0)
方案2:调整数据库初始值+生成器参数(快速适配)
无需自定义代码,通过参数偏移抵消逻辑差异:
- 调整数据库
hibernate_seq表中seq_value的值:将当前值减去allocationSize(如当前值为50则改为0) - 修改
@TableGenerator参数:
@TableGenerator(name = "tk_sequence", table = "hibernate_seq", pkColumnName = "seq_name", pkColumnValue = "seq_token", valueColumnName = "seq_value", allocationSize = 50, initialValue = -49)
- 配置优化器为
legacy-hilo:
spring.jpa.properties.hibernate.id.optimizer.pooled.preferred=legacy-hilo
此配置会让新生成器第一个区间为0-49,后续逻辑自动对齐旧生成器的区间划分。
方案3:原生SQL+触发器控制(最稳妥)
通过数据库层面逻辑完全复刻旧生成器行为:
- 创建存储过程,实现"读取当前seq_value生成ID区间,使用后递增seq_value"的逻辑
- 实体类ID字段配置调用存储过程:
@GeneratedValue(strategy = GenerationType.AUTO, generator = "custom-seq-generator") @GenericGenerator(name = "custom-seq-generator", strategy = "org.hibernate.id.enhanced.TableGenerator", parameters = { @Parameter(name = "table", value = "hibernate_seq"), @Parameter(name = "pk_column_name", value = "seq_name"), @Parameter(name = "pk_column_value", value = "seq_token"), @Parameter(name = "value_column_name", value = "seq_value"), @Parameter(name = "optimizer", value = "none"), @Parameter(name = "increment_size", value = "50") })
- 配合Oracle触发器,在插入数据前调用存储过程生成ID,插入完成后更新
seq_value。
验证要点
- 确认第一个ID区间为0-49,后续区间依次为50-99、100-149等,与旧生成器完全一致
- 多应用同时生成ID时无重复
- 应用重启后,ID生成从上次中断的区间继续,无断档或重复
内容的提问来源于stack exchange,提问作者BarbetNL
相关产品推荐
相关产品推荐

