MapStruct - 在一个地方工作,但不在另一个地方工作

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

过去几天我在使用mapstruct时遇到了问题。这本来应该是一项简单的工作,直到奇怪的事情开始发生。

我有 BusinessUnit (BU)、UserBusinessUnitRole (UBUR) 和 Role。

UBUR 有角色和BU。角色有BU。 BU什么都没有。

我正在尝试绘制它们的地图。我已经成功实施 UBUR 很长时间了。由于角色的变化,我必须重新实现角色映射器(角色在此之前没有 BU)。所以我开始这样做,现在我遇到了一个奇怪的异常。我将首先展示 BU 映射器、实体和 dto。然后是 UBUR,最后是角色(我遇到了问题)

BU实体:

@Entity
@Table(name = "BusinessUnits")
public class BusinessUnit {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @NotBlank
    private String name;

    @NotNull
    private TypeName type;

    @Nullable
    @ManyToOne
    @JoinColumn(name = "CompaniesId", referencedColumnName = "Id")
    @Cascade(CascadeType.MERGE)
    private BusinessUnit company;

    @Nullable
    @ManyToOne
    @JoinColumn(name = "ProjectsId", referencedColumnName = "Id")
    @Cascade(CascadeType.MERGE)
    private BusinessUnit project;

    @Nullable
    @ManyToOne
    @JoinColumn(name = "WhiteboardsId", referencedColumnName = "Id")
    @Cascade(CascadeType.MERGE)
    private Whiteboard whiteboard;

   //constructors, getters and setters

BU DTO:(每条记录都在不同的文件中。只需将其放在这里即可使问题“更短”)

@JsonDeserialize(using = BusinessUnitDTODeserializer.class)
public interface BusinessUnitDTO {

    Long id();

    String name();

    TypeName type();

    CompanyDTO company();

    ProjectDTO project();

    WhiteboardDTO whiteboard();

}

public record CompanyDTO(
        Long id,

        @NotNull
        @NotBlank
        String name,

        @NotNull
        TypeName type,

        @Nullable
        WhiteboardDTO whiteboard

) implements BusinessUnitDTO{

        public CompanyDTO company(){
                return null;
        }

        public ProjectDTO project(){
                return null;
        }

}

public record ProjectDTO(
        Long id,

        @NotNull
        @NotBlank
        String name,

        @NotNull
        TypeName type,

        @NotNull
        CompanyDTO company,

        @Nullable
        WhiteboardDTO whiteboard

) implements BusinessUnitDTO{

        public ProjectDTO project(){
                return null;
        }
}

public record TeamDTO(
        Long id,

        @NotNull
        @NotBlank
        String name,

        @NotNull
        TypeName type,

        @NotNull
        CompanyDTO company,

        @NotNull
        ProjectDTO project,

        @Nullable
        WhiteboardDTO whiteboard

) implements BusinessUnitDTO{
}

最后是映射器:

@Mapper(componentModel = "spring", uses = {WhiteboardMapper.class}, collectionMappingStrategy = CollectionMappingStrategy.TARGET_IMMUTABLE)
@Validated
public interface BusinessUnitMapper {

    @Named("toBusinessUnitDTO")
    default BusinessUnitDTO toBusinessUnitDTO(@Valid BusinessUnit businessUnit) {
        return switch (businessUnit.getType()) {
            case COMPANY -> toCompanyDTO(businessUnit);
            case PROJECT -> toProjectDTO(businessUnit);
            case TEAM -> toTeamDTO(businessUnit);
        };
    }

    @Named("toBusinessUnitDTOs")
    default List<BusinessUnitDTO> toBusinessUnitDTOs(@Valid Iterable<BusinessUnit> businessUnits) {
        if(businessUnits == null){
            return new ArrayList<>();
        }

        return StreamSupport.stream(businessUnits.spliterator(), false)
                .map(this::toBusinessUnitDTO)
                .collect(Collectors.toList());
    }

    @Named("toCompanyDTO")
    CompanyDTO toCompanyDTO(@Valid BusinessUnit businessUnit);

    @Named("toProjectDTO")
    @Mapping(target = "company", qualifiedByName = {"toCompanyDTO"})
    ProjectDTO toProjectDTO(@Valid BusinessUnit businessUnit);

    @Named("toTeamDTO")
    @Mapping(target = "company", qualifiedByName = {"toCompanyDTO"})
    @Mapping(target = "project", qualifiedByName = {"toProjectDTO"})
    TeamDTO toTeamDTO(@Valid BusinessUnit businessUnit);

    @Named("toBusinessUnitEntity")
    default BusinessUnit toBusinessUnitEntity(@Valid BusinessUnitDTO businessUnitDTO) {
        return switch (businessUnitDTO.type()) {
            case COMPANY -> toEntityFromCompanyDTO((CompanyDTO) businessUnitDTO);
            case PROJECT -> toEntityFromProjectDTO((ProjectDTO) businessUnitDTO);
            case TEAM -> toEntityFromTeamDTO((TeamDTO) businessUnitDTO);
        };
    }

    @Named("toBusinessUnitEntities")
    default List<BusinessUnit> toBusinessUnitEntities(@Valid Iterable<BusinessUnitDTO> businessUnitDTOs) {
        if(businessUnitDTOs == null){
            return new ArrayList<>();
        }

        return StreamSupport.stream(businessUnitDTOs.spliterator(), false)
                .map(this::toBusinessUnitEntity)
                .collect(Collectors.toList());
    }

    @Mapping(target = "company", ignore = true)
    @Mapping(target = "project", ignore = true)
    BusinessUnit toEntityFromCompanyDTO(@Valid CompanyDTO companyDTO);

    @Mapping(target = "project", ignore = true)
    BusinessUnit toEntityFromProjectDTO(@Valid ProjectDTO projectDTO);

    BusinessUnit toEntityFromTeamDTO(@Valid TeamDTO teamDTO);

}

UBUR 实体:

@Entity
@Table(name = "UsersBusinessUnitsRoles", uniqueConstraints = {@UniqueConstraint(columnNames = {"UsersId", "BusinessUnitsId", "RolesId"})})
public class UserBusinessUnitRole {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "UsersId", referencedColumnName = "Id")
    @Cascade(CascadeType.MERGE)
    private User user;

    @ManyToOne
    @JoinColumn(name = "BusinessUnitsId", referencedColumnName = "Id")
    @Cascade(CascadeType.MERGE)
    private BusinessUnit businessUnit;

    @ManyToOne
    @JoinColumn(name = "RolesId", referencedColumnName = "Id")
    @Cascade(CascadeType.MERGE)
    private Role role;
    
    //constructors, getters and setters

乌布尔DTO

public record UserBusinessUnitRoleDTO(

        Long id,

        @NotNull
        UserWithoutPasswordDTO user,

        @NotNull
        BusinessUnitDTO businessUnit,

        @NotNull
        RoleDTO role
) {

}

UBUR 映射器:

@Mapper(componentModel = "spring", uses = {UserMapper.class, BusinessUnitMapper.class, RoleMapper.class}, collectionMappingStrategy = CollectionMappingStrategy.TARGET_IMMUTABLE)
@Validated
public interface UsersBusinessUnitsRolesMapper {

    @Mapping(target = "user", qualifiedByName = {"toUserWithoutPasswordDTO"})
    @Mapping(target = "role", qualifiedByName = {"toRoleDTO"})
    @Mapping(target = "businessUnit", qualifiedByName = {"toBusinessUnitDTO"})
    UserBusinessUnitRoleDTO toDTO(@Valid UserBusinessUnitRole userBusinessUnitRole);

    List<UserBusinessUnitRoleDTO> toDTO(@Valid Iterable<UserBusinessUnitRole> usersBusinessUnitsRoles);

    @Mapping(target = "user", qualifiedByName = {"toUserEntity"})
    @Mapping(target = "role", qualifiedByName = {"toRoleEntity"})
    @Mapping(target = "businessUnit", qualifiedByName = {"toBusinessUnitEntity"})
    UserBusinessUnitRole toEntity(@Valid UserWithPassBusinessUnitRoleDTO userWithPassBusinessUnitRoleDTO);

    List<UserBusinessUnitRole> toEntity(@Valid Iterable<UserWithPassBusinessUnitRoleDTO> usersWithPassBusinessUnitsRolesDTOs);

}

令人困惑的部分来了。

角色实体:

@Entity
@Table(name = "Roles")
public class Role {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @NotBlank
    private String name;

    @NotNull
    @ManyToMany
    @JoinTable(
            name= "RolesAuthorities",
            joinColumns = @JoinColumn(name = "RolesId"),
            inverseJoinColumns = @JoinColumn(name = "AuthoritiesId"))
    private List<Authority> authorities;

    @NotNull
    @ManyToOne
    @JoinColumn(name = "BusinessUnitsId")
    @Cascade(CascadeType.MERGE)
    private BusinessUnit businessUnit;

    //constructors, getters and setters

角色 DTO:

public record RoleDTO (
        Long id,

        @NotNull
        String name,

        List<Authority> authorities,

        @NotNull
        BusinessUnitDTO businessUnit
) {
}

角色映射器:

@Mapper(componentModel = "spring", uses = {AuthorityMapper.class, BusinessUnitMapper.class})
@Validated
public interface RoleMapper {

    @Named("toRoleDTO")
    @Mapping(target = "businessUnit", qualifiedByName = {"toBusinessUnitDTO"})
    RoleDTO toDTO(@Valid Role role);

    List<RoleDTO> toDTO(@Valid Iterable<Role> roles);

    @Named("toRoleEntity")
    @Mapping(target = "businessUnit", qualifiedByName = {"toBusinessUnitEntity"})
    Role toEntity(@Valid RoleDTO role);

    List<Role> toEntity(@Valid Iterable<RoleDTO> roles);

}

我在每次“尝试”之前都会清理干净。 问题就在这里。在编译过程中,这一行 (

List<RoleDTO> toDTO(@Valid Iterable<Role> roles);
) 抛出两个异常:

java: The return type BusinessUnitDTO is an abstract class or interface. Provide a non abstract / non interface result type or a factory method.

java: Can't map property "BusinessUnit role.businessUnit" to "BusinessUnitDTO roleDTO.businessUnit". Consider to declare/implement a mapping method: "BusinessUnitDTO map(BusinessUnit value)".

如果我删除提到的行,一切都会编译。

  1. 实现基本上是一个 for 循环调用单个方法 n 次
  2. UBUR 映射器中的工作原理几乎相同

为什么它会在列表方法上抛出异常,而不是在单个方法上抛出异常? 问题是什么?为什么一个地方可以用,另一个地方就不行?

还尝试从mapstruct v1.5.3.Final升级到v.1.5.5.Final。没有帮助。

java mapstruct
1个回答
0
投票

您是否已明确从

BusinessUnit
映射到
BusinessUnitDTO
?尝试在
BusinessUnitMapper
中添加一个方法来执行此操作 - 当 MapStruct 在从
BusinessUnit
BusinessUnitDTO
的转换过程中遇到该类型时,可以将
Role
映射到
RoleDTO
。显式映射应该可以解决该问题。请记住对反向映射执行相同的操作。如果有需要,可以在
RoleMapper
添加相应的方法。

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