JPA使用@IdClass复合主键时如何避免手动设置的reqId被序列生成器覆盖
问题根因
- 标注了
@GeneratedValue的主键字段,Hibernate默认会忽略手动设置的值,始终使用主键生成器的输出覆盖字段原有值 - 现有JpaRepository泛型配置错误:使用
@IdClass配置复合主键时,JpaRepository第二个泛型参数应为复合主键类MyId而非Long,错误配置会导致主键判断逻辑异常
解决方案
方案一:自定义支持手动赋值的主键生成器(推荐)
修改你使用的StringPrefixedSequenceIdGenerator,重写generate方法,字段有值时直接返回,为空时才生成序列值:
public class StringPrefixedSequenceIdGenerator extends SequenceStyleGenerator { @Override public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException { if (object instanceof MyEntity) { MyEntity entity = (MyEntity) object; // reqId已手动赋值时直接返回 if (entity.getReqId() != null) { return entity.getReqId(); } } // 为空时走原有序列生成逻辑 return super.generate(session, object); } }
修改后无需调整原有实体注解和业务逻辑,新增场景reqId为空自动生成,更新场景手动赋值后不会被覆盖。
方案二:更新场景使用原生SQL插入
如果不想修改主键生成器,可在Repository中自定义原生插入方法,绕过Hibernate主键生成逻辑:
// Repository新增方法 @Modifying @Transactional @Query(value = "insert into my_entity(id, rev_no, 其他字段名) values (:reqId, :revNo, 其他参数)", nativeQuery = true) int insertNewVersion(@Param("reqId") String reqId, @Param("revNo") Long revNo, 其他参数);
更新新实体时调用该方法直接插入即可。
额外修正项
- 修正Repository和Service的泛型配置:
// 修正Repository @Repository public interface MyEntityRepository extends JpaRepository<MyEntity, MyId> { } // 修正Service父类泛型 public class MyEntityService extends BaseService<MyEntityRepository, MyEntity, MyId> { // 原有业务逻辑 }
- 确保复合主键类
MyId满足规范:实现Serializable接口、有公共无参构造方法、重写equals和hashCode方法,包含reqId和revisionNumber两个字段。 - 原有
@GeneratedValue策略配置有误:使用序列生成器时strategy应改为GenerationType.SEQUENCE而非GenerationType.IDENTITY。
内容的提问来源于stack exchange,提问作者sminmgr
相关产品推荐
相关产品推荐

