You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

含@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 02:07:19