¿Qué es MapStruct?
MapStruct es una herramienta que nos permite, mediante anotaciones crear mapeos entre objetos en tiempo de compilación. Evitando tener que escribir todo el código de mapeo a mano, está pensado para funcionar con Java 1.6 o posterior.
Añadir MapStruct a nuestro proyecto
Ahora veremos cómo incluir MapStruct en un proyecto con Maven, simulando conversiones entre objetos y objetos DTO, tal y como sucedería en una arquitectura REST.
Para añadir MapStruct añadimos la siguiente dependencia a nuestro proyecto:
Actualizamos los índices de Maven y con esto ya tenemos MapStruct listo para usarlo en nuestro proyecto, ahora veremos un ejemplo práctico.
Ejemplo con Java
Ahora vamos a crear una clase llamada Address y otra AddressDTO, que nos servirán para realizar los diferentes mapeos, vamos a crear varios ejemplos de mapeo por lo que iremos cambiando los atributos de las clases para cada ejemplo.
Ejemplo 1: Clases a mapear con los mismos atributos, tipos y nombres iguales.
En este primer ejemplo, veremos el caso más sencillo, en el que nuestra clase y nuestro DTO, tienen lo mismos campos, con los mismos tipos y los mismos nombres. Ambas clases tienen sus métodos getters, setters y constructor.
Una vez tenemos nuestras clases creadas, vamos a crear nuestro Mapper, que puede ser una interfaz o una clase abstracta, en este ejemplo utilizaremos la interfaz.
Una vez tenemos esta interfaz creada, necesitamos que al compilar nuestro proyecto Maven nos genere la implementación de la clase, nos vamos al pom.xml y en pluginManagement ponemos lo siguiente:
Ahora creamos una prueba unitaria para comprobar que funciona nuestro converter:
En el test creamos el objeto Address a convertir y el objeto AddressDTO que nos tendría que devolver para comprobar qué funciona correctamente, a su vez en el test hemos ‘instanciado’ el converter haciendo uso de MapStruct, con el método getMapper(). Ahora ejecutamos en nuestra terminal mvn clean verify y el test debería pasarnos, si vamos a la carpeta target, podemos ver la implementación de nuestro converter y lo que hace MapStruct por debajo.
Bien, hasta ahora hemos visto como mapear objetos idénticos, pero, ¿qué ocurre si tenemos los mismos tipos de atributos pero algunos con nombres distintos?
Ejemplo 2: Clases a mapear con los mismos atributos, tipos y algunos nombres diferentes.
Con la misma configuración de antes, dejamos la clase Address tal cual está, vamos a cambiar solo nuestra clase DTO, y vamos a dejarla así:
Como vemos, solo hemos cambiado de nombre los atributos phone -> phoneNumber y name -> personName, ahora si ejecutamos de nuevo nuestro test, fallará. Esto se debe a que MapStruct no está pudiendo realizar un mapeo directo, vamos a la interfaz y lo solucionamos de la siguiente manera:
De esta forma estamos diciéndole a MapStruct, que el atributo phone de Address, tiene que ser mapeado al atributo phoneNumber de AddressDTO, y lo mismo con name, como vemos es muy sencillo, pero que pasa si dentro del objeto a convertir tengo otro objeto que también necesita conversión, vamos a verlo en el tercer ejemplo:
Ejemplo 3: Clases a mapear con los mismos atributos, diferentes tipos y nombres iguales.
Vamos a poner los atributos de name, lastname y phone, dentro de un objeto llamado Person, y crearemos también un PersonDTO, vemos como quedarían nuestras clases:
Bien, ahora cambiamos nuestro test, para poder probar también los datos dentro del objeto Person:
Si ejecutamos esto, nuestros tests pasarán sin cambiar nada en MapStruct, porque es lo suficientemente ‘listo’ como para crear un mapeo directo entre Person y PersonDTO, pero si esos objetos fueran más complejos o tuviera dificultades MapStruct para mapearlos, podemos crear un método en la interfaz de esta forma:
O si la conversión fuera demasiado compleja, a partir de Java 8, podemos hacer uso de los métodos default en las interfaces y crear nosotros mismos a mano la conversión, pero esto solo en casos necesarios.
Conclusión
Como vemos, crear mapeos entre objetos con MapStruct es muy sencillo, también nos permite crear mapeos con objetos con herencia, con listas anidadas, etc, etc. Pero para todo ello ya existe la documentación de MapStruct en el siguiente enlace: MapStruct reference guide
Espero que te haya gustado este tutorial, nos vemos en el siguiente 🙂
Muy buen tutorial excelente, pregunta la interfaz en que paquete deberia estar, con la entidad o el dto, o en un paquete aparte para interfaces, muchas gracias
Muy bueno Juan Manuel!
Una duda, en los últimos 2 trozos de código el método no sería al revés, es decir, convertAdressToAdressDTO?
como usar target y source con una clase que hereda de otra ejemplo:
tengo personaAdadto que hereda de personadto que tiene el campos de
private PersonaIdDTO id;
a la vez id como es un objeto tiene Long id y Long idIndup como seria el mapeo con una entidad Persona que tiene private Long idApi; private Long indDupApi;