Below is an example of the entity to DTO mapping and mapper and generated Map Struct implementation.
public abstract class User {
private int id;
private String firstName;
private String lastName;
private int age;
private LocalDateTime dateOfBirth;
private List<Address> addresses;
}
public class Address {
private int userId;
private String streetAddress;
private String city;
private String state;
private String zipCode;
}
public class Employee extends User {
private String designation;
private Employee reportingManager;
}
public class Contractor extends User {
private String contractingCompanyName;
}
public abstract class UserDTO {
private int id;
private String firstName;
private String lastName;
private int age;
private LocalDateTime dateOfBirth;
}
public class EmployeeDTO extends UserDTO {
private String designation;
private EmployeeDTO reportingManager;
}
public class ContractorDTO extends UserDTO {
private String contractingCompanyName;
}
@Mapper(subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION)
public abstract class UserMapper {
@BeanMapping(ignoreByDefault = true)
@SubclassMapping(source = Employee.class, target = EmployeeDTO.class)
@SubclassMapping(source = Contractor.class, target = ContractorDTO.class)
@Mapping(source = "id", target = "id")
@Mapping(source = "firstName", target = "firstName")
@Mapping(source = "lastName", target = "lastName")
public abstract UserDTO map(User user);
}
Generated Mapper
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2023-07-14T12:56:15-0400",
comments = "version: 1.5.5.Final, compiler: javac, environment: Java 17.0.5 (Amazon.com Inc.)"
)
public class UserMapperImpl extends UserMapper {
@Override
public UserDTO map(User user) {
if ( user == null ) {
return null;
}
if (user instanceof Employee) {
return employeeToEmployeeDTO( (Employee) user );
}
else if (user instanceof Contractor) {
return contractorToContractorDTO( (Contractor) user );
}
else {
throw new IllegalArgumentException("Not all subclasses are supported for this mapping. Missing for " + user.getClass());
}
}
protected EmployeeDTO employeeToEmployeeDTO(Employee employee) {
if ( employee == null ) {
return null;
}
EmployeeDTO employeeDTO = new EmployeeDTO();
employeeDTO.setId( employee.getId() );
employeeDTO.setFirstName( employee.getFirstName() );
employeeDTO.setLastName( employee.getLastName() );
employeeDTO.setDesignation( employee.getDesignation() );
employeeDTO.setReportingManager( employeeToEmployeeDTO( employee.getReportingManager() ) );
return employeeDTO;
}
protected ContractorDTO contractorToContractorDTO(Contractor contractor) {
if ( contractor == null ) {
return null;
}
ContractorDTO contractorDTO = new ContractorDTO();
contractorDTO.setId( contractor.getId() );
contractorDTO.setFirstName( contractor.getFirstName() );
contractorDTO.setLastName( contractor.getLastName() );
contractorDTO.setContractingCompanyName( contractor.getContractingCompanyName() );
return contractorDTO;
}
I expected that adding BeanMapping#ignoreByDefault=true would only map the explicitly mapped properties. But that is not the case with the subclasses.
I was hoping for a better way other than explicitly mapping each subtype and annotating with InhertConfiguration and excluding the other properties from the subclasses.
As mentioned in MapStruct Discussion, this is a known bug in MapStruct versions up to 1.5.5 and is fixed in main (possibly in 1.6.0 Release)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With