Spring Boot3.1.4+Java21下替换Hibernate6.x废弃UUIDGenerator
Hibernate 6.x 替换废弃UUIDGenerator实现可选ID生成器
问题背景
我用Spring Boot 3.1.4 + Java 21开发,原来写了个继承UUIDGenerator的OptionalIdGenerator,用来实现可选ID生成——实体已有ID时返回原ID,没有就自动生成UUID。但Hibernate 6.0开始UUIDGenerator被废弃了,换实现后碰到了空指针异常。
原有废弃代码
package policy.generator; import org.hibernate.HibernateException; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.id.UUIDGenerator; import java.io.Serializable; public class OptionalIdGenerator extends UUIDGenerator { @Override public Serializable generate(SharedSessionContractImplementor session, Object obj) throws HibernateException { Serializable id = session.getEntityPersister(null, obj).getClassMetadata().getIdentifier(obj, session); return id != null ? id : super.generate(session, obj); } }
尝试的替代实现(触发空指针)
package policy.generator; import org.hibernate.HibernateException; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.id.IdentifierGenerator; import java.io.Serializable; import java.util.UUID; public class OptionalIdGenerator implements IdentifierGenerator { @Override public Serializable generate(SharedSessionContractImplementor session, Object obj) throws HibernateException { // 尝试通过持久化上下文获取ID Object idObject = session.getPersistenceContext().getEntry(obj).getId(); Serializable id = (idObject instanceof Serializable) ? (Serializable) idObject : null; if (id != null) { return id; } else { return UUID.randomUUID(); } } }
遇到的错误
Request processing failed: java.lang.NullPointerException: Cannot invoke "org.hibernate.engine.spi.EntityEntry.getId()" because the return value of "org.hibernate.engine.spi.PersistenceContext.getEntry(Object)" is null
解决方法
空指针的根源是:新创建的实体在插入数据库前,还没被加入持久化上下文,此时PersistenceContext.getEntry(obj)会返回null。正确的做法是通过实体的元数据(EntityPersister)获取当前ID,同时复用Hibernate官方的UUID生成逻辑,避免自己实现的潜在问题。
正确的实现代码
package policy.generator; import org.hibernate.HibernateException; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.uuid.UuidGenerator; import org.hibernate.persister.entity.EntityPersister; import java.io.Serializable; public class OptionalIdGenerator implements IdentifierGenerator { // 复用Hibernate官方的UUID生成器,保证行为和原生一致 private final UuidGenerator delegateUuidGenerator = new UuidGenerator(); @Override public Serializable generate(SharedSessionContractImplementor session, Object entity) throws HibernateException { // 通过EntityPersister获取实体当前ID,这和原有废弃代码的逻辑对齐,且更可靠 EntityPersister persister = session.getEntityPersister(null, entity); Serializable currentId = persister.getIdentifier(entity, session); if (currentId != null) { return currentId; } // 没有ID时调用官方生成器生成UUID return delegateUuidGenerator.generate(session, entity); } }
关键说明
- 避开持久化上下文的坑:新实体在未被持久化时不会出现在持久化上下文中,用
EntityPersister获取ID是稳定的方式,和原有逻辑兼容。 - 复用官方生成逻辑:Hibernate 6.x提供了
org.hibernate.id.uuid.UuidGenerator作为废弃类的替代,直接复用它能保证UUID生成的一致性,不用自己写UUID.randomUUID()。 - 无需修改实体配置:实体类的ID注解配置可以保持不变,比如:
@Id @GeneratedValue(generator = "optional-uuid") @GenericGenerator(name = "optional-uuid", strategy = "policy.generator.OptionalIdGenerator") private UUID id;
内容的提问来源于stack exchange,提问作者Leooavr
相关产品推荐
相关产品推荐

