Cómo mapear clases extendidas en MapStruct

Aug 23 2020

Tengo una pregunta sobre mapStruct. Tengo un caso en el que extiendo la clase desde la entidad base y no estoy seguro de cómo mapearla. Aquí está mi caso.

Entidad base:

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

BaseDto:

public class BaseDto {

    private Long id;
}

Entidad de usuario:

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

UsuarioDto:

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

Y el mapeador es así:

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

    User userDtoToUser(UserDto userDto);

    UserDto userToUserDto(User user);
}

Mapeador base:

@Mapper
public interface BaseMapper {

    BaseEntity dtoToEntity(BaseDto baseDto);

    BaseDto entityToDto(BaseEntity baseEntity);
}

El problema es que no obtengo la propiedad de ID asignada.

Gracias por tu tiempo.

EDITAR:

No se muestra ningún error, en la implementación del mapeador (código generado) no hay mapeo para esa ID:

 @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();
    }

Respuestas

2 Pankaj Aug 23 2020 at 19:51

Supongo (ya que no ha puesto buidercódigo) el problema es que su clase de constructor no incluye el campo de clase principal. MapStruct hace algunas suposiciones al generar código para mapper. De la documentación -

La implementación predeterminada de BuilderProvider asume lo siguiente:

  1. El tipo tiene un método de creación de constructor estático público sin parámetros que devuelve un constructor. Entonces, por ejemplo, Person tiene un método estático público que devuelve PersonBuilder.
  2. El tipo de constructor tiene un método público sin parámetros (método de construcción) que devuelve el tipo que se está construyendo. En nuestro ejemplo, PersonBuilder tiene un método que devuelve Person.
  3. En caso de que haya varios métodos de compilación, MapStruct buscará un método llamado compilación, si tal método existe, se usará este, de lo contrario, se creará un error de compilación.

Si está utilizando Lombok, puede resolver esto usando @SuperBuildercomo -

@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;
}

Y generado podría parecerse a -

@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();
}