Reputation: 1347
I'm using MapStruct v 1.4.1.Final to map Hibernate entities to corresponding DTOs. I'm using Spring Boot, so my mapper's classes have the componentModel
equals to spring
.
I have 2 entities, Person
and Address
. Person
contains an Address
instance:
Person
public class Person {
private String username;
private String name;
private Address address;
}
public class PersonDto {
private String id;
private String name;
private AddressDto address;
private String role;
}
Address
public class Address {
private String addr;
private String city;
}
public class AddressDto {
private String houseAddress;
private String city;
}
and here are the mappers I wrote:
Mappers
@Mapper(componentModel = "spring")
public interface AddressMapper {
@Mapping(source="person.username", target="userName")
@Mapping(source="address.addr", target="address")
DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Address address);
@Mapping(source="person.username", target="userName")
@Mapping(source="person.address.addr", target="address")
DeliveryAddressDto personToDeliveryAddressDto(Person person);
@Mapping(source = "address.addr", target = "houseAddress")
AddressDto addressToAddressDto(Address address);
@InheritInverseConfiguration(name = "addressToAddressDto")
Address addressDtoToAddress(AddressDto addressDto);
}
@Mapper(componentModel = "spring", uses = {AddressMapper.class})
public interface PersonMapper {
@Mapping(source = "id", target = "username")
@Mapping(source = "address.houseAddress", target = "address.addr")
@Mapping(source = "role", target = "role.id")
Person toPerson(PersonDto personDto);
List<Person> toPeople(List<PersonDto> personDtos);
@InheritInverseConfiguration(name = "toPerson")
PersonDto toPersonDto(Person person);
List<PersonDto> toPeopleDto(List<Person> people);
/**
* update an existing Person with Dto info.
* Set infos on the existing object (Person), without creating a new one.
*/
@Mapping(source = "role", target = "role.id")
void updatePersonFromDto(PersonDto personDto, @MappingTarget Person person);
}
where you can see that I said to the PersonMapper
to use the AddressMapper
to use its implementations.
However, for the conversion of the address to the address dto I found another implementation in the PersonMapper
class (same for the inverse mapping):
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2020-11-21T15:44:55+0100",
comments = "version: 1.4.1.Final, compiler: javac, environment: Java 11 (Oracle Corporation)"
)
@Component
public class AddressMapperImpl implements AddressMapper {
@Override
public DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Address address) {
if ( person == null && address == null ) {
return null;
}
DeliveryAddressDto deliveryAddressDto = new DeliveryAddressDto();
if ( person != null ) {
deliveryAddressDto.setUserName( person.getUsername() );
}
if ( address != null ) {
deliveryAddressDto.setAddress( address.getAddr() );
}
return deliveryAddressDto;
}
@Override
public DeliveryAddressDto personToDeliveryAddressDto(Person person) {
if ( person == null ) {
return null;
}
DeliveryAddressDto deliveryAddressDto = new DeliveryAddressDto();
deliveryAddressDto.setUserName( person.getUsername() );
deliveryAddressDto.setAddress( personAddressAddr( person ) );
return deliveryAddressDto;
}
@Override
public AddressDto addressToAddressDto(Address address) {
if ( address == null ) {
return null;
}
AddressDto addressDto = new AddressDto();
addressDto.setHouseAddress( address.getAddr() );
addressDto.setCity( address.getCity() );
return addressDto;
}
@Override
public Address addressDtoToAddress(AddressDto addressDto) {
if ( addressDto == null ) {
return null;
}
Address address = new Address();
address.setAddr( addressDto.getHouseAddress() );
address.setCity( addressDto.getCity() );
return address;
}
private String personAddressAddr(Person person) {
if ( person == null ) {
return null;
}
Address address = person.getAddress();
if ( address == null ) {
return null;
}
String addr = address.getAddr();
if ( addr == null ) {
return null;
}
return addr;
}
}
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2020-11-21T15:44:55+0100",
comments = "version: 1.4.1.Final, compiler: javac, environment: Java 11 (Oracle Corporation)"
)
@Component
public class PersonMapperImpl implements PersonMapper {
@Autowired
private AddressMapper addressMapper;
@Override
public Person toPerson(PersonDto personDto) {
if ( personDto == null ) {
return null;
}
Person person = new Person();
person.setAddress( addressDtoToAddress1( personDto.getAddress() ) );
person.setRole( personDtoToRole( personDto ) );
person.setUsername( personDto.getId() );
person.setName( personDto.getName() );
return person;
}
@Override
public List<Person> toPeople(List<PersonDto> personDtos) {
if ( personDtos == null ) {
return null;
}
List<Person> list = new ArrayList<Person>( personDtos.size() );
for ( PersonDto personDto : personDtos ) {
list.add( toPerson( personDto ) );
}
return list;
}
@Override
public PersonDto toPersonDto(Person person) {
if ( person == null ) {
return null;
}
PersonDto personDto = new PersonDto();
personDto.setAddress( addressToAddressDto1( person.getAddress() ) );
personDto.setId( person.getUsername() );
personDto.setRole( personRoleId( person ) );
personDto.setName( person.getName() );
return personDto;
}
@Override
public List<PersonDto> toPeopleDto(List<Person> people) {
if ( people == null ) {
return null;
}
List<PersonDto> list = new ArrayList<PersonDto>( people.size() );
for ( Person person : people ) {
list.add( toPersonDto( person ) );
}
return list;
}
@Override
public void updatePersonFromDto(PersonDto personDto, Person person) {
if ( personDto == null ) {
return;
}
if ( person.getRole() == null ) {
person.setRole( new Role() );
}
personDtoToRole1( personDto, person.getRole() );
person.setName( personDto.getName() );
person.setAddress( addressMapper.addressDtoToAddress( personDto.getAddress() ) );
}
protected Address addressDtoToAddress1(AddressDto addressDto) {
if ( addressDto == null ) {
return null;
}
Address address = new Address();
address.setAddr( addressDto.getHouseAddress() );
address.setCity( addressDto.getCity() );
return address;
}
protected Role personDtoToRole(PersonDto personDto) {
if ( personDto == null ) {
return null;
}
Role role = new Role();
role.setId( personDto.getRole() );
return role;
}
protected AddressDto addressToAddressDto1(Address address) {
if ( address == null ) {
return null;
}
AddressDto addressDto = new AddressDto();
addressDto.setHouseAddress( address.getAddr() );
addressDto.setCity( address.getCity() );
return addressDto;
}
private String personRoleId(Person person) {
if ( person == null ) {
return null;
}
Role role = person.getRole();
if ( role == null ) {
return null;
}
String id = role.getId();
if ( id == null ) {
return null;
}
return id;
}
protected void personDtoToRole1(PersonDto personDto, Role mappingTarget) {
if ( personDto == null ) {
return;
}
mappingTarget.setId( personDto.getRole() );
}
}
In this implementation, why the method addressDtoToAddress1()
is created? Why is not used the addressDtoToAddress()
provided in the AddressMapper
implementation? Those methods are identical.
Please also note that the AddressMapper
is injected in the PersonMapper
and the method addressDtoToAddress()
of the AddressMapper
is called in the method updatePersonFromDto()
.
Upvotes: 1
Views: 986
Reputation: 1575
You're specifying an address mapping twice in your PersonMapper
.
@Mapping(source = "id", target = "username")
@Mapping(source = "address.houseAddress", target = "address.addr")
Person toPerson(PersonDto personDto);
You're stating that PersonMapper
should use the AddressMapper
, but instead you add another override mapping to map the fields manually.
You can just remove the mapping and it will find the correct one by itself.
@Mapping(source = "id", target = "username")
Person toPerson(PersonDto personDto);
If you want to add an explicit mapping, you'd need to pick the source as PersonDto
's address field to target Person
's address field. Below will give the same result.
@Mapping(source = "id", target = "username")
@Mapping(source = "address", target = "address")
Person toPerson(PersonDto personDto);
It works fine in the updatePersonFromDto()
because you didn't add an override of it and it picked it up the AddressMapper
by itself. You'll notice the same behaviour if you add the same redundant mapping to it.
Upvotes: 2