如何实现带字符递增的高效Alphanumeric ID生成?Hibernate咨询
实现前缀字母自动递增的字母数字ID生成器
看起来你已经有了一个基础的ID生成器,但需要实现数字段到9999时前缀字母自动进位的逻辑(比如RxT-AA9999之后自动生成RxT-AB0001)。下面是具体的解决方案,基于你现有的Hibernate SequenceStyleGenerator扩展来改造:
核心思路
我们可以把ID拆成三部分:
- 固定前缀:比如
RxT-,这部分始终不变 - 可变字母段:从
AA开始,每10000个ID自动进位(AA→AB→...→AZ→BA→...→ZZ) - 数字段:从
0001到9999,循环递增,数字满了触发字母段进位
实现的关键是把数据库序列的连续值,映射成「字母段索引 + 数字段」的组合:
- 序列值
1~10000→ 字母段AA+ 数字0001~9999 - 序列值
10001~20000→ 字母段AB+ 数字0001~9999 - ...以此类推,直到
ZZ9999
修改后的ID生成器代码
public class UserIdGenerator extends SequenceStyleGenerator { // 固定前缀参数(比如"RxT-") public static final String FIXED_PREFIX_PARAMETER = "fixedPrefix"; public static final String FIXED_PREFIX_DEFAULT = ""; private String fixedPrefix; // 数字部分的位数(比如4位对应0001-9999) public static final String DIGIT_LENGTH_PARAMETER = "digitLength"; public static final int DIGIT_LENGTH_DEFAULT = 4; private int digitLength; private int maxDigitValue; // 计算得到的数字最大值,比如4位就是9999 @Override public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException { // 获取数据库序列的下一个值(从1开始) Long sequenceValue = (Long) super.generate(session, object); // 计算数字段:确保范围在1~maxDigitValue之间 long digitPart = (sequenceValue - 1) % maxDigitValue + 1; // 计算字母段的索引:每maxDigitValue个序列值,字母段进一位 long letterIndex = (sequenceValue - 1) / maxDigitValue; // 将索引转换为两位大写字母(0→AA,1→AB,...675→ZZ) String letterPart = convertIndexToLetters(letterIndex); // 格式化数字段为固定位数 String formattedDigit = String.format("%0" + digitLength + "d", digitPart); return fixedPrefix + letterPart + formattedDigit; } @Override public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException { super.configure(LongType.INSTANCE, params, serviceRegistry); fixedPrefix = ConfigurationHelper.getString(FIXED_PREFIX_PARAMETER, params, FIXED_PREFIX_DEFAULT); digitLength = ConfigurationHelper.getInt(DIGIT_LENGTH_PARAMETER, params, DIGIT_LENGTH_DEFAULT); maxDigitValue = (int) Math.pow(10, digitLength) - 1; // 计算数字最大值:比如4位→10^4-1=9999 } // 将数字索引转换为两位大写字母的工具方法 private String convertIndexToLetters(long index) { // 两位字母最多支持676种组合(AA~ZZ),超出可以扩展为三位字母 if (index < 0 || index > 675) { throw new IllegalArgumentException("Index out of range for two-letter prefix: " + index); } int firstLetterPos = (int) (index / 26); int secondLetterPos = (int) (index % 26); return String.valueOf((char) ('A' + firstLetterPos)) + (char) ('A' + secondLetterPos); } }
实体类的ID配置更新
把原来的固定前缀参数替换成新的配置项:
@Id @GeneratedValue(generator = "user_seq_generator", strategy = GenerationType.SEQUENCE) @GenericGenerator( name = "user_seq_generator", strategy = "user.util.UserIdGenerator", parameters = { @Parameter(name = SequenceStyleGenerator.INCREMENT_PARAM, value = "1"), // 设置固定前缀 @Parameter(name = UserIdGenerator.FIXED_PREFIX_PARAMETER, value = "RxT-"), // 设置数字部分的位数(4位) @Parameter(name = UserIdGenerator.DIGIT_LENGTH_PARAMETER, value = "4"), // 序列初始值设为1,保证第一个ID是RxT-AA0001 @Parameter(name = SequenceStyleGenerator.INITIAL_PARAM, value = "1") } ) @Column(name = "user_id") private String id;
扩展说明
- 如果需要支持三位字母前缀(比如
AAA→AAB→...→ZZZ),只需要修改convertIndexToLetters方法,增加对第三位字母的计算逻辑即可。 - 确保数据库中的序列初始值为1,否则第一个ID可能不符合预期。如果之前已经使用过序列,记得调整序列的当前值,避免ID重复。
- 字母段的进位逻辑是基于26进制的,完全符合你需要的
AA→AB→ZZ的递增规则。
内容的提问来源于stack exchange,提问作者Sunny Kumar
相关产品推荐
相关产品推荐

