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

MapStruct不支持泛型?如何转换MyBatis-Plus的IPage为PageDto

MapStruct原生支持泛型类型映射,你遇到的Can't generate mapping method for a generic type variable source报错,本质是泛型声明方式不符合MapStruct的编译期代码生成规则:MapStruct生成映射实现时需要明确的类型上下文,你写的两种泛型声明方式(接口层面声明无界泛型、方法层面声明无界泛型)都会让代码生成器无法识别泛型的实际类型边界,无法生成类型安全的转换逻辑。

错误写法的核心问题

  • 接口级泛型声明:MapStruct生成的Mapper实现类是普通具象Java类,无法为接口上声明的无界泛型参数生成统一的转换实现,不同业务实体到DTO的字段映射逻辑本身就存在差异,不存在通用的泛型实现逻辑。
  • 方法级泛型声明:直接在单个映射方法上声明独立泛型参数时,MapStruct不会将该泛型识别为可传递的类型变量,会直接判定为未知泛型源抛出错误。

可直接复用的实现方案

方案1:基础泛型Mapper + 业务Mapper继承(纯MapStruct生成,无手写逻辑)

先定义不带@Mapper注解的基础泛型分页映射接口,把分页公共字段的映射规则统一定义好:

public interface BasePageMapper<S, T> {
    @Mapping(source = "total", target = "total")
    @Mapping(source = "pages", target = "pages")
    @Mapping(source = "current", target = "pageNum")
    @Mapping(source = "size", target = "pageSize")
    @Mapping(source = "records", target = "records")
    PageDto<T> toPageDto(IPage<S> page);
}

编写具体业务的Mapper时,直接继承上述基础接口,指定明确的源实体、目标DTO类型,加上@Mapper注解即可。MapStruct会自动生成实现类,同时自动完成records列表内的实体到DTO的类型转换:

@Mapper(componentModel = "spring")
public interface UserMapper extends BasePageMapper<User, UserDto> {
    // 单实体转换方法,MapStruct会自动在分页转换时复用该方法处理records列表
    UserDto toUserDto(User user);
}

该方案完全由MapStruct生成所有转换逻辑,类型安全,无手写硬编码,适合规范统一的业务场景。

方案2:通用转换默认方法(灵活度最高,无需重复继承)

如果不想为每个业务分页单独写继承逻辑,可以在公共Converter里写通用分页转换的默认方法,手动处理确定类型的公共分页字段,把泛型的列表记录转换交给传入的函数式接口处理:

@Mapper(componentModel = "spring")
public interface CommonConverter {
    default <S, T extends Serializable> PageDto<T> convertPage(IPage<S> sourcePage, Function<S, T> recordConverter) {
        PageDto<T> result = new PageDto<>();
        result.setTotal(sourcePage.getTotal());
        result.setPages(sourcePage.getPages());
        result.setPageNum(sourcePage.getCurrent());
        result.setPageSize(sourcePage.getSize());
        if (sourcePage.getRecords() != null) {
            result.setRecords(sourcePage.getRecords().stream()
                    .map(recordConverter)
                    .toList());
        }
        return result;
    }
}

使用时直接注入公共Converter,传入对应业务Mapper的单实体转换方法引用即可:

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private CommonConverter commonConverter;

    public PageDto<UserDto> queryUserPage(Integer pageNum, Integer pageSize) {
        IPage<User> dbPage = userMapper.selectPage(new Page<>(pageNum, pageSize), Wrappers.emptyWrapper());
        return commonConverter.convertPage(dbPage, userMapper::toUserDto);
    }
}

该方案通用逻辑只需要编写一次,所有业务分页场景都可以复用,不需要重复写映射配置。

注意事项

  • 你的PageDto类声明了泛型边界<T extends Serializable>,使用时确保传入的目标DTO类型实现了Serializable接口,避免泛型边界不匹配的编译错误。
  • 不要直接在被@Mapper注解标记的接口上编写无绑定的接口级/方法级泛型映射方法,这类写法MapStruct无法在编译期推导实际类型,必然会抛出你遇到的泛型映射错误。

内容的提问来源于stack exchange,提问作者Jackeroo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 18:57:23