Spring Boot升级后Mapstruct出现属性映射方法歧义错误
问题解决:MapStruct映射Short到JPA实体时的方法歧义异常
问题描述
升级Spring Boot从2.1.0.RELEASE到2.7.2、Maven从3.0.0到3.8.6后,使用MapStruct 1.3.1.Final(升级到最新版仍存在)进行对象映射时,出现如下异常:
Ambiguous mapping methods found for mapping property "java.lang.Short TypeA" to TypeB: TypeB TypeBRepo.getOne(java.lang.Short id), TypeB TypeBRepo.getById(java.lang.Short id), TypeB TypeBRepogetReferenceById(java.lang.Short id)
相关代码结构:
- TypeB的Repository继承JpaRepository:
@Repository public interface TypeBRepo extends JpaRepository<TypeB, Short> { }
- MapStruct映射接口:
@Mappings({ @Mapping(target = "type_b_value", source = "request.TypeA"), // 注:原代码target字段名需与实体类对应 @Mapping(target = "securityValue", source = "request.currentPrice"), @Mapping(target = "user", source = "user"), @Mapping(target = "id", ignore = true), @Mapping(target = "documents", ignore = true), }) public abstract void sourceToDestination(final RequestType request, final UserType user, final @MappingTarget TargetA input);
- TargetA实体类:
@Entity @Getter @Setter @DiscriminatorValue("6") @FieldDefaults(level = AccessLevel.PRIVATE) public class TargetA extends BlaBla { @NotNull @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "investment_loan_type_id") TypeB type_b_value; }
问题原因
Spring Boot 2.7.x对应的Spring Data JPA版本中,JpaRepository接口新增了getById和getReferenceById方法,加上原本已过时的getOne,三个方法签名均为TypeB getXxx(Short id)。MapStruct自动推导Short类型ID到TypeB实体的转换逻辑时,无法确定使用哪个方法,因此抛出歧义异常。
解决方案
方案1:显式指定Repository方法
通过@Named标记目标方法,再在MapStruct映射中指定使用该方法:
- 给TypeBRepo的目标方法添加注解:
@Repository public interface TypeBRepo extends JpaRepository<TypeB, Short> { @Named("getTypeBById") @Override TypeB getById(Short id); }
- 在MapStruct映射配置中指定方法:
@Mappings({ @Mapping(target = "type_b_value", source = "request.TypeA", qualifiedByName = "getTypeBById"), // 其他映射配置... }) public abstract void sourceToDestination(final RequestType request, final UserType user, final @MappingTarget TargetA input);
方案2:自定义类型转换器
编写转换器手动实现ID到实体的转换,让MapStruct调用该转换器:
@Component public class ShortToTypeBConverter implements Converter<Short, TypeB> { private final TypeBRepo typeBRepo; public ShortToTypeBConverter(TypeBRepo typeBRepo) { this.typeBRepo = typeBRepo; } @Override public TypeB convert(Short source) { // 选择你需要的方法(如getById/getReferenceById) return typeBRepo.getById(source); } }
在MapStruct映射接口上指定使用该转换器:
@Mapper(componentModel = "spring", uses = ShortToTypeBConverter.class) public interface YourMapper { // 映射方法... }
方案3:手动实现映射逻辑
在抽象映射类中注入Repository,手动完成ID到实体的赋值:
@Mapper(componentModel = "spring") public abstract class YourMapper { @Autowired protected TypeBRepo typeBRepo; @Mappings({ @Mapping(target = "securityValue", source = "request.currentPrice"), @Mapping(target = "user", source = "user"), @Mapping(target = "id", ignore = true), @Mapping(target = "documents", ignore = true), }) public abstract void sourceToDestination(final RequestType request, final UserType user, final @MappingTarget TargetA input); @AfterMapping protected void setTypeB(RequestType request, @MappingTarget TargetA input) { input.setType_b_value(typeBRepo.getById(request.getTypeA())); } }
内容的提问来源于stack exchange,提问作者mster3313
相关产品推荐
相关产品推荐

