Spring Boot JPA中枚举前缀+数据库生成ID的复合@Id实现问询
实现Spring Boot + JPA 自定义复合字符串ID生成(前缀+序列)
针对你需要生成{固定长度序列值}-{枚举前缀}格式的复合ID需求,提供两种落地方案,均能保证事务内的ID唯一性与完整性:
方案一:自定义Hibernate IdentifierGenerator(推荐)
通过扩展Hibernate的ID生成器,直接集成到JPA的ID生成生命周期中,逻辑更规范且无需手动管理序列调用。
1. 实现自定义ID生成器
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.enhanced.SequenceStyleGenerator; import org.hibernate.service.ServiceRegistry; import org.hibernate.type.Type; import java.io.Serializable; import java.util.Properties; public class CompositePrefixIdGenerator implements IdentifierGenerator { private SequenceStyleGenerator sequenceGenerator; @Override public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) { // 复用Hibernate内置序列生成器,配置序列参数 sequenceGenerator = new SequenceStyleGenerator(); params.setProperty("sequence_name", "my_entity_seq"); // 数据库序列名称 params.setProperty("initial_value", "1"); params.setProperty("increment_size", "1"); sequenceGenerator.configure(type, params, serviceRegistry); } @Override public Serializable generate(SharedSessionContractImplementor session, Object obj) { if (!(obj instanceof MyEntity entity)) { throw new IllegalArgumentException("仅支持MyEntity类型的ID生成"); } // 生成序列值并格式化为5位固定长度字符串 Long sequenceVal = (Long) sequenceGenerator.generate(session, obj); String formattedSeq = String.format("%05d", sequenceVal); // 拼接枚举前缀生成最终ID return formattedSeq + "-" + entity.getMyEnum().getPrefix(); } }
2. 实体类配置
import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.Enumerated; import jakarta.persistence.EnumType; import org.hibernate.annotations.GenericGenerator; @Entity public class MyEntity { @Id @GenericGenerator( name = "composite-prefix-generator", type = CompositePrefixIdGenerator.class ) @GeneratedValue(generator = "composite-prefix-generator") private String compositeId; @Enumerated(EnumType.STRING) private MyEnum myEnum; // 构造器、getter、setter... // 注意:保存实体前必须设置myEnum字段,否则生成器会抛出异常 }
方案二:@PrePersist回调结合数据库序列(快速实现)
利用JPA的生命周期回调,手动调用数据库序列生成ID,逻辑更直观。
1. 创建数据库序列
以PostgreSQL为例,执行SQL创建序列:
CREATE SEQUENCE my_entity_seq START WITH 1 INCREMENT BY 1;
2. 实体类配置
import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.Enumerated; import jakarta.persistence.EnumType; import jakarta.persistence.PrePersist; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; @Entity public class MyEntity { @Id private String compositeId; @Enumerated(EnumType.STRING) private MyEnum myEnum; @PersistenceContext private EntityManager entityManager; @PrePersist public void generateCompositeId() { // 仅在ID未设置且枚举值存在时生成 if (compositeId == null && myEnum != null) { // 获取序列下一个值 Long sequenceVal = (Long) entityManager.createNativeQuery("SELECT nextval('my_entity_seq')").getSingleResult(); String formattedSeq = String.format("%05d", sequenceVal); compositeId = formattedSeq + "-" + myEnum.getPrefix(); } } // 构造器、getter、setter... }
关键注意事项
- 必须提前设置枚举值:两种方案都依赖实体的
myEnum字段值,保存前务必赋值,否则会生成失败 - 唯一性保障:数据库序列本身是线程安全且事务安全的,能保证ID全局唯一
- 格式调整:
%05d控制序列值的固定长度,可根据业务需求修改位数(如%08d生成8位数字) - 多序列扩展:若不同枚举类型需要独立序列,可在自定义生成器中根据
myEnum动态切换序列名称
内容的提问来源于stack exchange,提问作者CastPer
相关产品推荐
相关产品推荐

