如何在Hibernate中实现可复用的通用自定义ID生成器
如何在Hibernate中实现可复用的通用自定义ID生成器
我太懂你想要的效果了——就是能在不同实体类里靠一个简单注解,比如@CustomId(name = "USR"),自动生成带指定前缀的唯一ID,不用每个实体都重复写生成逻辑对吧?其实Hibernate本身就提供了扩展点来实现这个需求,我来一步步拆解说清楚:
第一步:定义你的自定义@CustomId注解
首先得把这个注解“造”出来,还要用Hibernate的@IdGeneratorType把它和后面要写的生成器绑定在一起,这样Hibernate才知道这个注解要对应哪个生成逻辑:
import org.hibernate.annotations.IdGeneratorType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.FIELD; // 标记这个注解只能用在字段上 @Target(FIELD) // 让注解在运行时能被Hibernate读取到 @Retention(RetentionPolicy.RUNTIME) // 绑定后面要实现的生成器类 @IdGeneratorType(CustomPrefixIdGenerator.class) public @interface CustomId { // 用来指定ID的前缀,比如"USR"、"ORD" String name(); // 可选参数:指定用哪个数据库序列生成数字部分,默认用global_id_seq String sequenceName() default "global_id_seq"; }
第二步:实现自定义ID生成器类
接下来就是核心的生成逻辑了,我们要写一个类实现Hibernate的IdentifierGenerator接口,把前缀和自增数字拼接起来。这里我用数据库序列来保证数字部分的唯一性,这样生成的ID既带前缀又不会重复:
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.enhanced.SequenceStyleGenerator; import org.hibernate.persister.entity.EntityPersister; import java.io.Serializable; import java.lang.reflect.Field; import java.util.Properties; public class CustomPrefixIdGenerator implements IdentifierGenerator { // 复用Hibernate自带的序列生成器来处理数字自增部分 private SequenceStyleGenerator sequenceGenerator; @Override public void configure(org.hibernate.type.Type type, Properties params, org.hibernate.service.ServiceRegistry serviceRegistry) throws org.hibernate.id.IdentifierGeneratorException { // 初始化序列生成器,不用自己写自增逻辑 sequenceGenerator = new SequenceStyleGenerator(); sequenceGenerator.configure(type, params, serviceRegistry); } @Override public Serializable generate(SharedSessionContractImplementor session, Object entity) throws org.hibernate.HibernateException { try { // 获取当前实体的元数据,找到ID字段的名字(不一定叫id哦) EntityPersister persister = session.getEntityPersister(null, entity); String idFieldName = persister.getIdentifierPropertyName(); // 反射获取ID字段上的@CustomId注解 Field idField = entity.getClass().getDeclaredField(idFieldName); idField.setAccessible(true); CustomId customId = idField.getAnnotation(CustomId.class); if (customId == null) { throw new IllegalArgumentException("实体类的ID字段必须标记@CustomId注解"); } // 从注解里拿到指定的前缀 String prefix = customId.name(); // 用序列生成器拿到自增的唯一数字 Long sequenceValue = (Long) sequenceGenerator.generate(session, entity); // 格式化成你要的样式,比如USR000001,补零长度可以自己调整 return String.format("%s%06d", prefix, sequenceValue); } catch (NoSuchFieldException | IllegalAccessException e) { throw new org.hibernate.HibernateException("生成自定义ID失败", e); } } }
第三步:在实体类里愉快复用
现在就可以在各个实体类里轻松使用这个注解了,完全不用重复写生成逻辑:
import jakarta.persistence.Entity; import jakarta.persistence.Id; @Entity public class User { @Id @CustomId(name = "USR", sequenceName = "user_id_seq") // 指定专属序列 private String id; // 其他字段、getter、setter... } @Entity public class Order { @Id @CustomId(name = "ORD") // 用默认的global_id_seq序列 private String id; // 其他字段、getter、setter... }
一些要注意的小细节
- 数据库里要提前创建对应的序列,比如
global_id_seq、user_id_seq,如果你的Hibernate配置了hbm2ddl.auto=create或者update,Hibernate会自动帮你创建序列(前提是数据库支持序列,比如PostgreSQL、Oracle)。 - 要是不想用数据库序列,也可以换成UUID生成唯一部分,比如把
sequenceValue换成UUID.randomUUID().toString().substring(0, 8),不过这样ID就没有自增的顺序性了,看业务需求选择就行。 - 要是需要更复杂的格式,比如前缀+日期+数字,只需要在
generate方法里修改拼接逻辑,非常灵活。
这样一套下来,你就有了一个完全可复用的自定义ID生成器,每个实体类加个@CustomId注解就能搞定,完美符合你的需求!
内容来源于stack exchange
相关产品推荐
相关产品推荐

