如何在Spring Boot与Hibernate中使用随机唯一数值作为主键?
实现Spring Boot + MySQL 随机唯一数值主键(非UUID、自增类型)
JPA/Hibernate没有直接提供随机数值类型的主键生成器,但可以通过两种实用方式实现需求:
方案一:自定义Hibernate主键生成器
通过实现Hibernate的IdentifierGenerator接口,自定义生成逻辑,确保生成的数值唯一且符合需求:
- 编写自定义生成器类
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.id.IdentifierGenerator; import java.security.SecureRandom; public class RandomNumericIdGenerator implements IdentifierGenerator { private static final SecureRandom RANDOM = new SecureRandom(); // 定义数值范围,比如10位数字,降低重复概率 private static final long MIN = 1000000000L; private static final long MAX = 9999999999L; @Override public Object generate(SharedSessionContractImplementor session, Object object) { // 生成随机数,循环校验确保数据库中不存在 long id = MIN + ((long) (RANDOM.nextDouble() * (MAX - MIN))); while (isIdExists(session, object.getClass(), id)) { id = MIN + ((long) (RANDOM.nextDouble() * (MAX - MIN))); } return id; } private boolean isIdExists(SharedSessionContractImplementor session, Class<?> entityClass, long id) { return session.createQuery("select count(e) from " + entityClass.getSimpleName() + " e where e.id = :id") .setParameter("id", id) .uniqueResult() != 0L; } }
- 在实体类中绑定生成器
import jakarta.persistence.*; import org.hibernate.annotations.GenericGenerator; @Entity @Table(name = "target_table") public class TargetEntity { @Id @GenericGenerator(name = "random_numeric_id", strategy = "com.your.package.RandomNumericIdGenerator") @GeneratedValue(generator = "random_numeric_id") @Column(name = "id", nullable = false, unique = true) private Long id; // 其他字段、getter/setter }
方案二:手动生成主键(简单场景推荐)
在业务层手动生成随机数值,设置到实体后再保存,依赖数据库唯一约束兜底保障唯一性:
- 实体类主键配置(关闭自动生成)
import jakarta.persistence.*; @Entity @Table(name = "target_table") public class TargetEntity { @Id @Column(name = "id", nullable = false, unique = true) private Long id; // 其他字段、getter/setter }
- 业务层生成唯一ID并保存
import org.springframework.stereotype.Service; import java.security.SecureRandom; @Service public class TargetEntityService { private static final SecureRandom RANDOM = new SecureRandom(); private static final long MIN = 1000000000L; private static final long MAX = 9999999999L; private final TargetEntityRepository repository; public TargetEntityService(TargetEntityRepository repository) { this.repository = repository; } public TargetEntity saveEntity(TargetEntity entity) { Long uniqueId = generateUniqueId(); entity.setId(uniqueId); return repository.save(entity); } private Long generateUniqueId() { Long id; do { id = MIN + ((long) (RANDOM.nextDouble() * (MAX - MIN))); } while (repository.existsById(id)); return id; } }
关键注意事项
- 字段类型:MySQL侧用
BIGINT存储主键,避免数值溢出 - 唯一性兜底:必须给主键字段添加
UNIQUE约束,即使代码层做了校验,也能在极端场景拦截重复插入 - 性能优化:如果数据量极大,可改用Redis缓存已使用的ID,减少数据库查询次数
内容的提问来源于stack exchange,提问作者ayush
相关产品推荐
相关产品推荐

