过去几天我在使用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)".
如果我删除提到的行,一切都会编译。
为什么它会在列表方法上抛出异常,而不是在单个方法上抛出异常? 问题是什么?为什么一个地方可以用,另一个地方就不行?
还尝试从mapstruct v1.5.3.Final升级到v.1.5.5.Final。没有帮助。
您是否已明确从
BusinessUnit
映射到 BusinessUnitDTO
?尝试在 BusinessUnitMapper
中添加一个方法来执行此操作 - 当 MapStruct 在从 BusinessUnit
到 BusinessUnitDTO
的转换过程中遇到该类型时,可以将 Role
映射到 RoleDTO
。显式映射应该可以解决该问题。请记住对反向映射执行相同的操作。如果有需要,可以在RoleMapper
添加相应的方法。