含@ManyToOne映射的复合键类型转换异常排查求助
问题排查:Spring无法将Long转换为IrMatchrules实体的异常
异常信息
Caused by: java.lang.IllegalStateException: Cannot convert value of type 'java.lang.Long' to required type 'com.mfs.ir.domain.IrMatchrules' for property 'matchrule': no matching editors or conversion strategy found at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:262) at org.springframework.beans.AbstractNestablePropertyAccessor.convertIfNecessary(AbstractNestablePropertyAccessor.java:590)
问题场景
配置看似无误,但触发上述类型转换异常,相关代码如下:
测试类代码
@ActiveProfiles("it") @RunWith(SpringRunner.class) @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @SpringBootTest public class IrMrIterationMappingIT { @Autowired private IterationMappingRepository iterationMappingRepository; @Autowired private MatchrulesRepository matchrulesRepository; @Test @Transactional public void shouldHaveRecordSavedCorrectly() { IrMatchrules inputIrMatchrules = matchrulesRepository.save( IrMatchrules.builder() .matchruleId(1L) .portfolioCode("PCODE") .build()); IrMrIterationMapping inputIrMrIterationMapping = IrMrIterationMapping.builder() .iterationType(MatchRuleIterationType.MANY_MANY) .matchrule(inputIrMatchrules) .build(); iterationMappingRepository.save(inputIrMrIterationMapping); IrMrIterationMapping result = iterationMappingRepository.findById( IrMrIterationMappingPK.builder() .iterationType(MatchRuleIterationType.MANY_MANY) .matchrule(inputIrMatchrules) .build() ).orElseThrow(); assertThat(result.getIterationType()).isEqualTo(inputIrMrIterationMapping.getIterationType()); } @After public void tearDown() { iterationMappingRepository.deleteAllInBatch(); matchrulesRepository.deleteAllInBatch(); } }
复合主键类代码
public class IrMrIterationMappingPK implements Serializable { @Id @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "MATCHRULE_ID") private IrMatchrules matchrule; @Id @Convert(converter = MatchRuleIterationTypeConverter.class) @Column(name = "ITERATION_ID") private MatchRuleIterationType iterationType; }
实体类代码
@Data @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode @Builder(toBuilder = true) @Entity @Table(name = "IR_MATCHRULES", schema = "RECON_OWNER", catalog = "") public class IrMatchrules { @Id @Column(name = "MATCHRULE_ID") private long matchruleId; @Column(name="MATCHRULE_NAME") private String matchruleName; @Column(name="ITERATION_TYPE") private Long iterationType; }
问题原因
核心错误出在复合主键类IrMrIterationMappingPK的设计上:
JPA要求复合主键(实现Serializable的主键类)必须使用基本数据类型、包装类或其他可序列化的简单类型,不能直接在主键字段上使用@ManyToOne关联实体类。
当JPA尝试从数据库读取MATCHRULE_ID字段(Long类型)时,会试图将其转换为IrMatchrules实体,但Spring没有默认的转换器支持这种转换,因此抛出异常。
解决方案
1. 修改复合主键类,使用基本类型作为主键字段
将IrMrIterationMappingPK中的IrMatchrules matchrule替换为Long matchruleId,并确保重写equals()和hashCode()方法(主键类必须实现这两个方法):
public class IrMrIterationMappingPK implements Serializable { @Id @Column(name = "MATCHRULE_ID") private Long matchruleId; @Id @Convert(converter = MatchRuleIterationTypeConverter.class) @Column(name = "ITERATION_ID") private MatchRuleIterationType iterationType; // 重写equals和hashCode @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; IrMrIterationMappingPK that = (IrMrIterationMappingPK) o; return Objects.equals(matchruleId, that.matchruleId) && iterationType == that.iterationType; } @Override public int hashCode() { return Objects.hash(matchruleId, iterationType); } }
2. 调整关联实体的映射逻辑
在IrMrIterationMapping实体类中,添加@ManyToOne关联到IrMatchrules,并设置insertable = false, updatable = false(因为主键字段matchruleId已经由@EmbeddedId管理):
@Entity @Table(name = "IR_MR_ITERATION_MAPPING", schema = "RECON_OWNER") @Data @Builder @NoArgsConstructor @AllArgsConstructor public class IrMrIterationMapping { @EmbeddedId private IrMrIterationMappingPK id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "MATCHRULE_ID", insertable = false, updatable = false) private IrMatchrules matchrule; // 其他字段和方法 }
3. 更新测试代码中的查询逻辑
构建主键时使用matchruleId而非实体对象:
IrMrIterationMapping result = iterationMappingRepository.findById( IrMrIterationMappingPK.builder() .iterationType(MatchRuleIterationType.MANY_MANY) .matchruleId(inputIrMatchrules.getMatchruleId()) .build() ).orElseThrow();
内容的提问来源于stack exchange,提问作者Ahmet Karakaya
相关产品推荐
相关产品推荐

