MapStruct

Permite mapear de forma rápida y segura objetos entre sí, sin tener que codificar a mano.

Generalmente, usamos DTOs para aislar un objeto de negocio de un objeto que se conecta con las bases de datos (@Entity).

Cuando hacemos una transformación de estos objetos, es útil usar MapStruct.

Configuración

<dependency>  
 <groupId>org.mapstruct</groupId>  
 <artifactId>mapstruct</artifactId>  
 <version>1.3.0.Final</version>  
</dependency>

Además, Mapstruct realiza mapeos en tiempos de compilación, ocupa el generate-sources de maven, que genera las clases de forma automática, así que necesitamos agregar un plugin, que funcione también con lombok:

<plugin>  
 <groupId>org.apache.maven.plugins</groupId>  
 <artifactId>maven-compiler-plugin</artifactId>  
 <version>3.5.1</version>  
 <configuration>  
     <source>1.8</source>  
     <target>1.8</target>  
     <annotationProcessorPaths>  
         <path>  
         <groupId>org.mapstruct</groupId>  
         <artifactId>mapstruct-processor</artifactId>  
         <version>${mapstruct.version}</version>  
         </path>  
         <path>  
         <groupId>org.projectlombok</groupId>  
         <artifactId>lombok</artifactId>  
         <version>${lombok.version}</version>  
         </path>  
     </annotationProcessorPaths>  
     <compilerArgs>  
     <compilerArg>-Amapstruct.defaultComponentModel=spring</compilerArg>  
     </compilerArgs>  
     </configuration>  
</plugin>

Configuración en los IDEs

  • Eclipse: Instalar plugin para permitir habilitar el procesamiento de anotaciones Plugin m2e y el plugin de MapStruct
  • IntelliJ: Sólo habilitar el procesamiento de anotaciones: Settings/Build, Execution, Deployment/Compiler/Annotation Processors

Usos

  1. Mapeo Simple

La primera opción es tener un Bean y un DTO iguales.
Por ejemplo:

@Data  
@AllArgsConstructor  
@NoArgsConstructor  
@Builder  
public class Beer {  
  private UUID id;  
 private String beerName;  
 private String beerStyle;  
 private Long upc;  
}

y el DTO:

@Getter  
@Setter  
@NoArgsConstructor  
@AllArgsConstructor  
@Builder  
public class BeerDTO {  
 private UUID id;  
 private String beerName;  
 private String beerStyle;  
 private Long upc;  

}

La forma de mapear es la siguiente:

  • Crear un package de mappers: En nuestro proyecto creamos un package mappers para almacenar todos nuestros objetos que harán el trabajo de mapear.
  • Crear una interfaz Mapper: En este caso creamos una interfaz BeerMapper, con una anotación de Mapstruct: @Mapper.
  • Crear métodos de conversion BeerToBeerDTO y BeerDTOToBeer: Estos son los métodos que van a convertir nuestros objetos.
@Mapper  
public interface BeerMapper {
//Instance nos permitirá usar el mapper en nuestras clases. 
BeerMapper INSTANCE = Mappers.getMapper(BeerMapper.class);  
  BeerDTO beerToBeerDTO(Beer beer);  
  Beer beerDTOToBeerDTO(BeerDTO beerDTO);  
}
  • El INSTANCE nos va a permitir llamar a la instancia del Mapper para utilizar los métodos.

Con esto, al momento de compilar, hará todo el proceso de mapeo automáticamente.

Finalmente, para convertir el objeto Bean a nuestro DTO simplemente hacemos lo siguiente:

BeerDTO beerDto = BeerMapper.INSTANCE.beerToBeerDTO(beer);

Y esto nos retornará el objeto mapeado.

2) Mapeo con campos distintos

El siguiente ejemplo es cuando tenemos un Bean con campos distintos a los que hay en el DTO, Por ejemplo:

public class EmployeeDTO {
private int employeeId;
private String employeeName;
// getters and setters
}

public class Employee {
private int id;
private String name;
// getters and setters
}

Cuando se mapean objetos distintos, se debe configurar el campo de origen a su campo de destino, para ello utilizamos la anotación @Mapping:

@Mapper
public interface EmployeeMapper {
    @Mappings({
      @Mapping(target="employeeId", source="entity.id"),
      @Mapping(target="employeeName", source="entity.name")
    })
    EmployeeDTO employeeToEmployeeDTO(Employee entity);
    @Mappings({
      @Mapping(target="id", source="dto.employeeId"),
      @Mapping(target="name", source="dto.employeeName")
    })
    Employee employeeDTOtoEmployee(EmployeeDTO dto);
}

Acá básicamente le decimos por ejemplo, que el campo employeeId debe mapearlo a id.

3) Mapeo de Beans con Beans Hijos

Otro ejemplo usual es cuando un Bean tiene una referencia a otros bean:

public class EmployeeDTO {
    private int employeeId;
    private String employeeName;
    private DivisionDTO division;
    // getters and setters
}

public class Employee {
    private int id;
    private String name;
    private Division division;
    // getters and setters
}

public class Division {
    private int id;
    private String name;
    //Getters and Setters
}

Por ejemplo, en el caso anterior tenemos Employee, que tiene objeto de Division.

Mapper para este Ejemplo

Para este caso, debemos agregar el mapper del objeto Division y al revés.
MapStruct detectará que el objeto que necesita ser convertido tiene otro objeto del cual existe mapeo, hará la tarea por nosotros.

Solo debemos agregar el Mapper que no tenemos:

DivisionDTO divisionToDivisionDTO(Division entity);
Division divisionDTOtoDivision(DivisionDTO dto);

Y con esto, ya podemos convertir el objeto.

Otros

Hay varias opciones más que explicaré más adelante.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: