为使用 Lombok 的 @Builder 注释的通用 DTO 生成映射器时,Mapstruct 无法解析类型 T(T 无法解析为类型)

问题描述 投票:0回答:1

我在 Spring Boot 应用程序中使用以下 Lombok 和 Mapstruct 版本:

  • 龙目岛 30.18.1
  • Mapstruct 1.5.5.Final
  • lombok-mapstruct-绑定 0.2.0

这就是我的 pom.xml 中注释处理器路径的配置方式:

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <source>21</source>
        <target>21</target>
        <annotationProcessorPaths>
          <path>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
          </path>
          <path>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok-mapstruct-binding</artifactId>
            <version>0.2.0</version>
          </path>
          <path>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>1.5.5.Final</version>
          </path>
        </annotationProcessorPaths>
      </configuration>
    </plugin>
  </plugins>
</build>

我有一个 Java 记录作为 DTO,表示结果页面,并具有泛型类型 T(@Builder 和 @With 是 Lombok 注释):

@Builder
@With
public record PageDto<T extends BaseDto>(
        int pageNumber,
        int pageSize,
        int pageNumberOfElements,
        long totalElements,
        int totalPages,
        List<T> content
        ) {
    
    public PageDto {
        
        if(content == null) {
            content = new ArrayList<>();
        }
    }
}

我使用Mapstruct将Spring接口org.springframework.data.domain.Page映射到我的PageDto,如下所示:

  • 我所有映射器的基本接口:
public interface BaseMapper<M extends BaseEntity, D extends BaseDto> {

  @Mapping(target = "pageNumber", expression = "java(entity.getNumber())")
    @Mapping(target = "pageSize", expression = "java(entity.getSize())")
    @Mapping(target = "pageNumberOfElements", expression = "java(entity.getNumberOfElements())")
    @Mapping(target = "totalElements", expression = "java(entity.getTotalElements())")
    @Mapping(target = "totalPages", expression = "java(entity.getTotalPages())")
    @Mapping(target = "content", source = "content")
    PageDto<D> toDto(Page<M> entity);
}
  • 以及扩展 BaseMapper 的 Mapstruct 映射器:
@Mapper(componentModel = "spring")
public interface UserMapper extends BaseMapper<UserEntity, UserDto> {}

但是生成的 Mapstruct 代码存在编译时错误:

T cannot be resolved to a type

Bellow 是生成的具体 Mapstruct

UserMapperImpl
:

public class UserMapperImpl implements UserMapper {

    @Override
    public PageDto<UserDto> toDto(Page<UserEntity> entity) {
        if ( entity == null ) {
            return null;
        }
        
        //this line causes a compile-time error: T cannot be resolved to a type
        PageDto.PageDtoBuilder<T> pageDto = PageDto.builder();

        if ( entity.hasContent() ) {
            pageDto.content( userEntityListToBaseDtoList( entity.getContent() ) );
        }

        pageDto.pageNumber( entity.getNumber() );
        pageDto.pageSize( entity.getSize() );
        pageDto.pageNumberOfElements( entity.getNumberOfElements() );
        pageDto.totalElements( entity.getTotalElements() );
        pageDto.totalPages( entity.getTotalPages() );

        return pageDto.build();
    }
}

这是生成错误的行:

PageDto.PageDtoBuilder<T> pageDto = PageDto.builder();
看起来 Mapstruct 没有解析泛型类型 T。 正确的生成代码应该是
PageDto.PageDtoBuilder<UserDto> pageDto = PageDto.builder();

我该如何解决这个问题?

提前致谢。

在网上搜索了一些类似的问题,但没有找到。

java spring-boot generics lombok mapstruct
1个回答
0
投票

这是因为 MapStruct 与 Lombok 的

@Builder
@With
注释一起使用时生成代码和处理泛型类型的方式。需要明确告知 MapStruct 泛型类型
T
。 重构
PageDto
以避免直接将 Lombok 的
@Builder
与泛型一起使用。 Lombok 的 @Builder 已知泛型问题。处理此问题的一种方法是创建一个如下所示的自定义构建器:

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.ArrayList;
import java.util.List;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class PageDto<T extends BaseDto> {
    private int pageNumber;
    private int pageSize;
    private int pageNumberOfElements;
    private long totalElements;
    private int totalPages;
    private List<T> content = new ArrayList<>();

    public static <T extends BaseDto> PageDtoBuilder<T> builder() {
        return new PageDtoBuilder<>();
    }

    public static class PageDtoBuilder<T extends BaseDto> {
        private int pageNumber;
        private int pageSize;
        private int pageNumberOfElements;
        private long totalElements;
        private int totalPages;
        private List<T> content;

        PageDtoBuilder() {
        }

        public PageDtoBuilder<T> pageNumber(int pageNumber) {
            this.pageNumber = pageNumber;
            return this;
        }

        public PageDtoBuilder<T> pageSize(int pageSize) {
            this.pageSize = pageSize;
            return this;
        }

        public PageDtoBuilder<T> pageNumberOfElements(int pageNumberOfElements) {
            this.pageNumberOfElements = pageNumberOfElements;
            return this;
        }

        public PageDtoBuilder<T> totalElements(long totalElements) {
            this.totalElements = totalElements;
            return this;
        }

        public PageDtoBuilder<T> totalPages(int totalPages) {
            this.totalPages = totalPages;
            return this;
        }

        public PageDtoBuilder<T> content(List<T> content) {
            this.content = content;
            return this;
        }

        public PageDto<T> build() {
            return new PageDto<>(pageNumber, pageSize, pageNumberOfElements, totalElements, totalPages, content);
        }
    }
}

然后更新映射器:

@Mapper(componentModel = "spring")
public interface UserMapper extends BaseMapper<UserEntity, UserDto> {

    @Override
    @Mapping(target = "pageNumber", expression = "java(entity.getNumber())")
    @Mapping(target = "pageSize", expression = "java(entity.getSize())")
    @Mapping(target = "pageNumberOfElements", expression = "java(entity.getNumberOfElements())")
    @Mapping(target = "totalElements", expression = "java(entity.getTotalElements())")
    @Mapping(target = "totalPages", expression = "java(entity.getTotalPages())")
    @Mapping(target = "content", source = "content")
    PageDto<UserDto> toDto(Page<UserEntity> entity);
}

自定义构建器模式可确保在创建

T
实例时使用正确的泛型类型
PageDto
。 通过显式定义
UserMapper
中的映射,MapStruct可以正确生成实现代码。

© www.soinside.com 2019 - 2024. All rights reserved.