使用@IdClass注解的JPA实体调用CrudRepository删除报incorrect PK错误
根因分析
Spring Data JPA 的 DefaultJpaEntityInformation 实现类处理单字段 @IdClass 主键场景时,会默认直接返回实体类中标注 @Id 的单个字段原始值,不会自动封装为你定义的 PrimaryKey 主键类实例。尽管你在 Repository 泛型上声明了主键类型为 PrimaryKey,但执行 entityInformation.getId(entity) 调用时,框架识别到实体仅存在一个 @Id 字段,就直接返回了该字段的 String 类型值,传入 em.find 方法时就和预期的 PrimaryKey 类型不匹配,触发类型错误。
解决方案
方案一:改用 @EmbeddedId 注解(推荐,兼容性最优)
如果允许调整实体注解规范,这是最稳妥的修复方式:
- 给
PrimaryKey类添加@Embeddable注解 - 修改实体类的主键声明,替换
@IdClass和单独的@Id字段:
@Entity @Table(name = "myentity") @JsonIgnoreProperties(ignoreUnknown = true) @Data @NoArgsConstructor @AllArgsConstructor public class MyEntity { @EmbeddedId private PrimaryKey id; // 其他非主键字段 }
修改后框架会正确识别主键类型为 PrimaryKey,默认的 delete、deleteById 方法都可以正常运行。
方案二:保留 @IdClass 规范,自定义删除逻辑
如果必须遵循全实体统一使用 @IdClass 的规范,不需要修改现有注解结构,可以用两种方式规避:
- Service 层直接调用 EntityManager 执行删除,绕过 Spring Data JPA 默认的 delete 方法校验:
public void destroy(final PrimaryKey pk) { MyEntity myEntity = repository.findById(pk) .orElseThrow(() -> new IllegalArgumentException(DOES_NOT_EXIST)); // 直接用EntityManager移除,不需要调用repository.delete em.remove(em.contains(myEntity) ? myEntity : em.merge(myEntity)); }
- 自定义 Repository 实现类,重写
delete、deleteById方法,手动将获取到的单字段主键值封装为PrimaryKey实例后再执行后续逻辑。
内容的提问来源于stack exchange,提问作者payne
相关产品推荐
相关产品推荐

