最近几天我在使用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不关心它。
1条答案
按热度按时间h6my8fg21#
你有没有明确地从
BusinessUnit
Map到BusinessUnitDTO
?尝试在BusinessUnitMapper
中添加一个方法来实现这一点-这允许MapStruct在从Role
到RoleDTO
的转换过程中遇到该类型时将BusinessUnit
Map到BusinessUnitDTO
。显式Map应该可以解决这个问题。记住对反向Map也要这样做。如有必要,可以向RoleMapper
添加相应的方法。