Jak mapować klasy rozszerzone w MapStruct

Aug 23 2020

Mam pytanie dotyczące mapStruct. Mam przypadek, w którym rozszerzam klasę z jednostki bazowej i nie jestem pewien, jak ją zmapować. Oto moja sprawa.

BaseEntity:

public class BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    @Column(name = "id")
    private Long id;
}

BaseDto:

public class BaseDto {

    private Long id;
}

UserEntity:

public class User extends BaseEntity {
    private String name;
    private String lastName;
    private String username;
    private String password;
    private String profilePicturePath;
}

UserDto:

public class UserDto extends BaseDto {
    private String name;
    private String lastName;
    private String username;
    private String password;
    private String profilePicturePath;
}

Mapper wygląda tak:

@Mapper(uses = {BaseMapper.class})
public interface UserMapper {

    User userDtoToUser(UserDto userDto);

    UserDto userToUserDto(User user);
}

BaseMapper:

@Mapper
public interface BaseMapper {

    BaseEntity dtoToEntity(BaseDto baseDto);

    BaseDto entityToDto(BaseEntity baseEntity);
}

Problem polega na tym, że nie mam odwzorowanej właściwości ID.

Dziękuję za Twój czas.

EDYTOWAĆ:

Nie jest wyświetlany żaden błąd, w implementacji programu mapującego (wygenerowanym kodzie) nie ma mapowania dla tego identyfikatora:

 @Override
    public User userDtoToUser(UserDto userDto) {
        if ( userDto == null ) {
            return null;
        }

        UserBuilder user = User.builder();

        user.name( userDto.getName() );
        user.lastName( userDto.getLastName() );
        user.username( userDto.getUsername() );
        user.password( userDto.getPassword() );
        user.profilePicturePath( userDto.getProfilePicturePath() );

        return user.build();
    }

Odpowiedzi

2 Pankaj Aug 23 2020 at 19:51

Zgaduję (ponieważ nie umieściłeś buiderkodu) problem polega na tym, że twoja klasa buildera nie zawiera pola klasy nadrzędnej. MapStruct robi pewne założenie podczas generowania kodu dla mappera. Z dokumentacji -

Domyślna implementacja BuilderProvider zakłada, że:

  1. Typ ma metodę tworzenia publicznego statycznego konstruktora bez parametrów, która zwraca konstruktora. Na przykład Person ma publiczną metodę statyczną, która zwraca PersonBuilder.
  2. Typ konstruktora ma bezparametrową metodę publiczną (metodę kompilacji), która zwraca budowany typ. W naszym przykładzie PersonBuilder ma metodę zwracającą Person.
  3. W przypadku, gdy istnieje wiele metod kompilacji, MapStruct będzie szukał metody o nazwie build, jeśli taka metoda istnieje, zostanie użyta, w przeciwnym razie zostanie utworzony błąd kompilacji.

Jeśli używasz Lombok, możesz rozwiązać ten problem, używając @SuperBuilderjako -

@SuperBuilder
@Getter
@ToString
public class UserDto extends BaseDto {
  private String name;
  private String lastName;
  private String username;
  private String password;
  private String profilePicturePath;
}

@Getter
@SuperBuilder
class BaseDto {
  private Long id;
}

@SuperBuilder
@Getter
@ToString
public class User extends BaseEntity {
  private String name;
  private String lastName;
  private String username;
  private String password;
  private String profilePicturePath;
}

@Setter
@Getter
@SuperBuilder
class BaseEntity {
  private Long id;
}

Wygenerowany mógłby wyglądać następująco -

@Override
public User userDtoToUser(UserDto userDto) {
    if ( userDto == null ) {
        return null;
    }

    UserBuilder<?, ?> user = User.builder();

    user.id( userDto.getId() );
    user.name( userDto.getName() );
    user.lastName( userDto.getLastName() );
    user.username( userDto.getUsername() );
    user.password( userDto.getPassword() );
    user.profilePicturePath( userDto.getProfilePicturePath() );

    return user.build();
}