You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
    }
}

关键说明

  1. 避开持久化上下文的坑:新实体在未被持久化时不会出现在持久化上下文中,用EntityPersister获取ID是稳定的方式,和原有逻辑兼容。
  2. 复用官方生成逻辑:Hibernate 6.x提供了org.hibernate.id.uuid.UuidGenerator作为废弃类的替代,直接复用它能保证UUID生成的一致性,不用自己写UUID.randomUUID()。
  3. 无需修改实体配置:实体类的ID注解配置可以保持不变,比如:
@Id
@GeneratedValue(generator = "optional-uuid")
@GenericGenerator(name = "optional-uuid", strategy = "policy.generator.OptionalIdGenerator")
private UUID id;

内容的提问来源于stack exchange,提问作者Leooavr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 19:20:16