JPA(EclipseLink)如何实现按COMPANY_NR分组自增DELIVERY_NR主键
解决方案
JPA 标准的 @GeneratedValue 序列生成策略本身不支持按其他字段分组生成独立序列,你可以通过以下两种适配 EclipseLink 的方案实现需求:
前置错误修正
你当前代码里的 @IdClass(value = Delivery.class) 配置不符合 JPA 规范,复合主键对应的主键类必须是独立的可序列化类,不能是实体类本身,先修正该配置:
// 独立复合主键类 public class DeliveryId implements Serializable { private String deliveryNr; private String companyNr; // 必须提供无参构造、全参构造 // 必须重写equals()和hashCode()方法 }
实体类上修改为 @IdClass(DeliveryId.class)
方案一:业务层生成主键(实现简单、兼容性好)
去掉 deliveryNr 字段上的 @GeneratedValue 和 @SequenceGenerator 注解,在保存 Delivery 实体的业务逻辑前,先查询对应 companyNr 下的最大 deliveryNr,加1后赋值再保存:
// 业务层保存逻辑示例 @Transactional public Delivery saveDelivery(Delivery delivery) { // 加悲观写锁避免并发场景下生成重复ID TypedQuery<Integer> maxQuery = em.createQuery( "SELECT COALESCE(MAX(CAST(d.deliveryNr AS integer)), 0) + 1 FROM Delivery d WHERE d.companyNr = :companyNr", Integer.class ); maxQuery.setParameter("companyNr", delivery.getCompanyNr()); maxQuery.setLockMode(LockModeType.PESSIMISTIC_WRITE); Integer nextNr = maxQuery.getSingleResult(); delivery.setDeliveryNr(String.valueOf(nextNr)); return em.merge(delivery); }
方案二:EclipseLink 自定义主键生成器(逻辑封装在持久层,业务层无感知)
- 自定义序列生成类,实现 EclipseLink 的
Sequence接口,逻辑为按传入的companyNr生成对应分组的自增ID - 在实体类上通过
@GeneratedValue指定自定义生成器:
// 自定义序列生成器 public class GroupedSequence extends Sequence { @Override public Object getGeneratedValue(Accessor accessor, WriteObjectQuery writeQuery) throws DatabaseException { Delivery entity = (Delivery) writeQuery.getObject(); String companyNr = entity.getCompanyNr(); // 此处逻辑和方案一的查询逻辑一致,查询对应companyNr的最大deliveryNr加1 // 省略重复的查询代码,最终返回生成的deliveryNr } // 其他必须实现的方法可按需返回默认值 }
实体类配置修改:
@Id @GeneratedValue(generator = "grouped-delivery-generator") @org.eclipse.persistence.annotations.SequenceGenerator( name = "grouped-delivery-generator", sequenceName = "dummy", // 无实际作用,占位即可 sequenceClass = GroupedSequence.class ) @Column(name = "DELIVERY_NR", nullable = false) private String deliveryNr;
高并发场景优化
如果业务并发量很高,可以额外创建一张序列维护表 COMPANY_DELIVERY_SEQ(COMPANY_NR, CURRENT_MAX_NR),每次生成ID时原子更新该表的 CURRENT_MAX_NR 字段,避免每次查询主表的最大值,提升性能。
内容的提问来源于stack exchange,提问作者Manu
相关产品推荐
相关产品推荐

