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
相关产品推荐
相关产品推荐

