
本项目代码:https://gitee.com/yunjiao-source/tutorials4j/tree/master/framework
在现代分布式系统架构中,数据库主键的生成策略是一个基础且关键的设计环节。传统自增主键在分库分表场景下容易产生冲突,而 UUID 虽然全局唯一却存在索引性能差、存储空间大等问题。雪花算法(Snowflake)凭借其全局唯一、趋势递增、高性能的特点,成为分布式 ID 生成的理想方案。
本文介绍一个为 Hibernate 框架定制的雪花算法主键生成器实现,通过自定义注解 + JPA 集成的方式,让开发者只需一行注解即可为实体主键注入分布式唯一 ID。
整个实现由三个核心组件构成:
组件 | 职责 |
|---|---|
| 自定义注解,标记实体的主键字段或 getter 方法 |
| Hibernate 主键生成器实现类,根据属性类型返回 String 或 Long 型 ID |
| 雪花算法工具类,封装 Hutool 的 Snowflake 实现,支持通过环境变量配置 workerId / datacenterId |
三者协作流程如下:
实体类 @SnowflakeIDGenerator → Hibernate 识别注解 → 调用 SnowflakeIdentifierGenerator.generate() → SnowflakeUtils.nextId()/nextIdStr() → 返回唯一 ID-D 参数)传入,支持不同节点部署时分配不同 ID。nextId() 返回 long 型数值 IDnextIdStr() 返回 19 位十进制字符串 IDpublic class SnowflakeUtils {
public static final String PRO_WORKER_ID = "TUTORIALS4J_SNOWFLAKE_WORKER_ID";
public static final String PRO_DATACENTER_ID = "TUTORIALS4J_SNOWFLAKE_DATACENTER_ID";
private Snowflake snowflake;
private static final SnowflakeUtils INSTANCE = new SnowflakeUtils();
private SnowflakeUtils() {
initSnowflake();
}
private synchronized void initSnowflake() {
// 从系统属性读取 workerId 和 datacenterId,默认均为 1
snowflake = IdUtil.getSnowflake(workerId, datacenterId);
}
public static long nextId() {
return INSTANCE.nextId_();
}
public static String nextIdStr() {
return INSTANCE.nextIdStr_();
}
}部署时通过 JVM 参数指定节点标识:
java -DTUTORIALS4J_SNOWFLAKE_WORKER_ID=2 -DTUTORIALS4J_SNOWFLAKE_DATACENTER_ID=1 -jar your-app.jar若未配置,默认 workerId=1,datacenterId=1。生产环境建议使用配置中心或环境变量动态注入。
@IdGeneratorType(SnowflakeIdentifierGenerator.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface SnowflakeIDGenerator {
}@IdGeneratorType 是 Hibernate 提供的元注解,用于将自定义注解与具体的 IdentifierGenerator 实现类关联。该类实现了 Hibernate 的 IdentifierGenerator 和 StandardGenerator 接口。构造函数接收三个参数:
SnowflakeIDGenerator config:注解实例(当前无属性,但可用于后续扩展)Member idMember:主键对应的字段或方法反射对象CustomIdGeneratorCreationContext creationContext:创建上下文构造函数中通过 Member 获取主键的运行时类型,并缓存在 propertyType 字段中。
@Override
public Object generate(SharedSessionContractImplementor session, Object object) {
if (String.class.isAssignableFrom(propertyType)) {
return SnowflakeUtils.nextIdStr();
}
return SnowflakeUtils.nextId();
}⚠️ 关键设计:生成器会根据实体主键声明的类型自动返回相应格式的 ID:
String → 返回 19 位字符串 ID(如 "1702334941288374272")Long、long、Integer 等)→ 返回 long 型数值(Hibernate 会自动处理类型转换,但推荐使用 Long)if (idMember instanceof Method) {
propertyType = ((Method) idMember).getReturnType();
} else {
propertyType = ((Field) idMember).getType();
}该写法兼容了字段(FIELD)和属性访问(PROPERTY)两种 JPA 主键映射方式。
@Entity
@Table(name = "t_order")
public class Order {
@Id
@SnowflakeIDGenerator
private Long id; // 生成 long 型 ID
private String orderNo;
@Column(name = "create_time")
private LocalDateTime createTime;
// getters and setters...
}若需要字符串类型主键:
@Id
@SnowflakeIDGenerator
private String id; // 自动生成 "1702334941288374272" 格式Order order = new Order();
order.setOrderNo("ORD-20250101-001");
session.save(order); // id 字段被自动填充无需手动调用任何 ID 生成方法,Hibernate 在 save 或 persist 时自动触发生成器。
特性 | 说明 |
|---|---|
零侵入 | 只需一个注解,无需修改业务代码 |
类型智能适配 | 根据主键类型自动选择返回 String 或 Long |
高性能 | 本地生成雪花 ID,无网络 IO,单机可达百万级 QPS |
分布式友好 | 通过 workerId / datacenterId 隔离不同节点 |
Hibernate 规范集成 | 使用官方 |
Snowflake 默认未处理时钟回拨。若服务器时间被手动回拨,可能产生重复 ID。建议配合 NTP 服务做时钟同步或者增加回拨容忍逻辑。Integer 类型且雪花 ID 超过 Integer.MAX_VALUE,会抛出异常。建议统一使用 Long 或 String。本文介绍了一套完整的 Hibernate 雪花算法主键生成器实现方案,涵盖了注解定义、类型适配、工具类封装及配置方式。该方案已在生产环境中稳定运行,显著简化了分布式 ID 生成的工作。开发者只需引入依赖并添加 @SnowflakeIDGenerator,即可享受高性能、全局唯一的 ID 生成能力。
依赖声明:本实现基于
cn.hutool: hutool-core和 Hibernate 6.x / JPA 3.2。请确保项目中已引入对应依赖。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。