
本文详解 spring data jpa 复合主键类中误用 @id 标注关联实体导致的类型转换异常(“no matching editors or conversion strategy found”),并提供正确建模复合主键、外键关系及双向映射的完整实践方案。
本文详解 spring data jpa 复合主键类中误用 @id 标注关联实体导致的类型转换异常(“no matching editors or conversion strategy found”),并提供正确建模复合主键、外键关系及双向映射的完整实践方案。
在使用 Spring Data JPA 定义复合主键(@EmbeddedId 或 @IdClass)时,一个常见但极易被忽视的错误是:在复合主键类中,对 @ManyToOne 关联字段错误地添加 @Id 注解。这会导致 JPA 尝试将整个实体对象(如 IrMatchrules)作为主键的一部分进行序列化/反序列化,而 Spring 的 TypeConverter 无法自动将 Long 类型 ID 值转换为复杂实体对象,从而抛出 IllegalStateException: Cannot convert value of type 'java.lang.Long' to required type 'com.mfs.ir.domain.IrMatchrules'。
根本原因在于:@Id 仅应标注主键字段本身(如 matchruleId: Long),而非关联实体引用。复合主键中的外键字段需通过 @ManyToOne + @JoinColumn 正确定义关系,其值应由 JPA 自动管理(即根据 matchrule.matchruleId 映射到数据库列),而非手动设为实体对象。
✅ 正确做法如下:
-
修正复合主键类 IrMrIterationMappingPK
移除 matchrule 字段上的 @Id,仅保留 @ManyToOne 和 @JoinColumn,明确其为关联映射而非主键字段(主键逻辑由组合属性共同构成,但 matchrule 本身不是“ID值”,而是“关联引用”):
public class IrMrIterationMappingPK implements Serializable {
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "MATCHRULE_ID", referencedColumnName = "MATCHRULE_ID")
private IrMatchrules matchrule; // ✅ 不加 @Id!
@Column(name = "ITERATION_ID")
@Convert(converter = MatchRuleIterationTypeConverter.class)
private MatchRuleIterationType iterationType;
// 必须提供无参构造、equals/hashCode、serialVersionUID(已由 Lombok @Data 覆盖)
}-
确保主实体 IrMatchrules 具备标准 JPA 主键结构
IrMatchrules 已正确定义 @Id(matchruleId),无需额外修改;但若需维护双向关系,可选添加 @OneToMany(注意:不建议在复合主键类上直接双向映射,而应在拥有方实体中定义):
@Entity
@Table(name = "IR_MATCHRULES", schema = "RECON_OWNER")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class IrMatchrules {
@Id
@Column(name = "MATCHRULE_ID")
@EqualsAndHashCode.Include
private long matchruleId;
@Column(name = "MATCHRULE_NAME")
private String matchruleName;
@Column(name = "ITERATION_TYPE")
private Long iterationType;
// 双向关系(可选,便于导航查询)
@OneToMany(
mappedBy = "id.matchrule", // 指向 IrMrIterationMapping 的复合主键字段
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
orphanRemoval = true
)
private List<IrMrIterationMapping> iterationMappings = new ArrayList<>();
}-
调整拥有方实体 IrMrIterationMapping(关键!)
复合主键类 IrMrIterationMappingPK 应作为嵌入式主键,而 IrMrIterationMapping 实体需使用 @EmbeddedId 并正确定义关系:
@Entity
@Table(name = "IR_MR_ITERATION_MAPPING", schema = "RECON_OWNER")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class IrMrIterationMapping {
@EmbeddedId
private IrMrIterationMappingPK id;
// 若需其他业务字段,可在此添加
// 构造函数辅助(便于测试)
public IrMrIterationMapping(IrMatchrules matchrule, MatchRuleIterationType iterationType) {
this.id = new IrMrIterationMappingPK();
this.id.setMatchrule(matchrule);
this.id.setIterationType(iterationType);
}
}⚠️ 注意事项:
- 禁止在 @EmbeddedId 类中对 @ManyToOne 字段加 @Id:这是引发本异常的直接原因;
- @JoinColumn 的 referencedColumnName 必须与被引用实体的 @Id 字段名一致(此处为 "MATCHRULE_ID");
- 测试中 iterationMappingRepository.findById(...) 需传入正确的 IrMrIterationMappingPK 实例(含已持久化的 IrMatchrules 对象),而非仅 ID;
- 若使用 @IdClass 替代 @EmbeddedId,逻辑相同:@IdClass 类中只声明主键属性类型,关联字段仍需 @ManyToOne + @JoinColumn,不可加 @Id。
通过以上重构,JPA 将正确识别 MATCHRULE_ID 为外键列,并在保存/查询时自动处理 Long ↔ IrMatchrules 的转换,彻底解决类型转换失败问题。

















