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

x6h2sr28  于 2023-10-14  发布在  Java
关注(0)|答案(1)|浏览(108)

最近几天我在使用mapstruct时遇到了问题。这本来是一个简单的工作,直到一些奇怪的事情开始发生。
我有业务单元(BU)、用户业务单元角色(UBUR)和角色。
UBUR有角色和BU。角色是BU。波士顿没有任何东西。
我在试着绘制它们的Map。我已经成功地实施了UBUR很长一段时间。由于角色的变化,我不得不重新实现角色Map器(在此之前角色没有BU)。所以我开始这样做,现在我得到了一个奇怪的异常。我将首先展示BUMap器、实体和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:

@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

UBUR DTO

public record UserBusinessUnitRoleDTO(

        Long id,

        @NotNull
        UserWithoutPasswordDTO user,

        @NotNull
        BusinessUnitDTO businessUnit,

        @NotNull
        RoleDTO role
) {

}

UBURMap器:

@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
) {
}

角色Map器:

@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);

}

我mvn清洁之前,每一个“尝试”。问题就在这里在编译期间,这一行(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次
1.在UBURMap器中,
为什么它在列表方法上抛出异常,而不是单个方法?有什么问题吗?为什么它在一个地方工作,而不是另一个?
还尝试从mapstruct v1.5.3.Final升级到v.1.5.5.Final。没什么用
找到了一些东西(我想)。MapStruct正在做我没有告诉它做的事情。所以我进入了map struct的实现。我看到了我对UBURMap器的期望。然后我进入角色实现,看到一些奇怪的东西

protected BusinessUnit businessUnitDTOToBusinessUnit(BusinessUnitDTO businessUnitDTO) {
        if ( businessUnitDTO == null ) {
            return null;
        }

        BusinessUnit businessUnit = new BusinessUnit();

        return businessUnit;
    }

    protected Role roleDTOToRole(RoleDTO roleDTO) {
        if ( roleDTO == null ) {
            return null;
        }

        Role role = new Role();

        role.setId( roleDTO.id() );
        role.setName( roleDTO.name() );
        List<Authority> list = roleDTO.authorities();
        if ( list != null ) {
            role.setAuthorities( new ArrayList<Authority>( list ) );
        }
        role.setBusinessUnit( businessUnitDTOToBusinessUnit( roleDTO.businessUnit() ) );

        return role;
    }

所以第二个列表方法(dto to实体)调用roleDTOToRole,roleDTOToRole调用businessUnitDTOToBusinessUnit。正常的方法调用它们的父方法,所以出于某种原因,我告诉mapstruct使用x方法,但它试图创建自己的实现(而不是使用已经创建的实现),并悲惨地失败了。
好吧,这是因为@Named注解。现在只需要弄清楚如何告诉mapstruct不关心它。

h6my8fg2

h6my8fg21#

你有没有明确地从BusinessUnitMap到BusinessUnitDTO?尝试在BusinessUnitMapper中添加一个方法来实现这一点-这允许MapStruct在从RoleRoleDTO的转换过程中遇到该类型时将BusinessUnitMap到BusinessUnitDTO。显式Map应该可以解决这个问题。记住对反向Map也要这样做。如有必要,可以向RoleMapper添加相应的方法。

相关问题