MapStruct es un generador de código que simplifica la implementación de mapeos entre objetos de Java permitiendo realizar este proceso de forma rápida y sencilla. Comúnmente en aplicaciones multicapa de Java se suele requerir mapear diferentes modelos de objetos, por ejemplo, entidades y DTO. Si se realiza de una manera manual puede ser tedioso y propenso a errores. Este es el propósito de MapStruct, simplificar el proceso de mapeo entre objetos. A diferencia de otras librerías, MapStruct genera los mapeos en tiempo de compilación, lo que permite un mejor rendimiento y una comprobación de errores más rápida.
MapStruct al ser un procesador de anotaciones se puede utilizar desde la línea de comandos usando Maven o Gradle, así como desde un IDE como IntelliJ IDEA o Eclipse.
Instalación
Para este ejemplo vamos a utilizar Maven, por lo que es necesario agregar lo siguiente al archivo pom.xml
.
<span><properties></span><span><org.mapstruct.version></span>1.6.3<span></org.mapstruct.version></span><span></properties></span><span><dependencies></span><span><dependency></span><span><groupId></span>org.mapstruct<span></groupId></span><span><artifactId></span>mapstruct<span></artifactId></span><span><version></span>${org.mapstruct.version}<span></version></span><span></dependency></span><span></dependencies></span><span><build></span><span><plugins></span><span><plugin></span><span><groupId></span>org.apache.maven.plugins<span></groupId></span><span><artifactId></span>maven-compiler-plugin<span></artifactId></span><span><version></span>3.8.1<span></version></span><span><configuration></span><span><source></span>1.8<span></source></span> <span><!-- Dependiendo de tu proyecto --></span><span><target></span>1.8<span></target></span> <span><!-- Dependiendo de tu proyecto --></span><span><annotationProcessorPaths></span><span><path></span><span><groupId></span>org.mapstruct<span></groupId></span><span><artifactId></span>mapstruct-processor<span></artifactId></span><span><version></span>${org.mapstruct.version}<span></version></span><span></path></span><span><!-- Otras dependencias de procesadores de anotaciones --></span><span></annotationProcessorPaths></span><span></configuration></span><span></plugin></span><span></plugins></span><span></build></span><span><properties></span> <span><org.mapstruct.version></span>1.6.3<span></org.mapstruct.version></span> <span></properties></span> <span><dependencies></span> <span><dependency></span> <span><groupId></span>org.mapstruct<span></groupId></span> <span><artifactId></span>mapstruct<span></artifactId></span> <span><version></span>${org.mapstruct.version}<span></version></span> <span></dependency></span> <span></dependencies></span> <span><build></span> <span><plugins></span> <span><plugin></span> <span><groupId></span>org.apache.maven.plugins<span></groupId></span> <span><artifactId></span>maven-compiler-plugin<span></artifactId></span> <span><version></span>3.8.1<span></version></span> <span><configuration></span> <span><source></span>1.8<span></source></span> <span><!-- Dependiendo de tu proyecto --></span> <span><target></span>1.8<span></target></span> <span><!-- Dependiendo de tu proyecto --></span> <span><annotationProcessorPaths></span> <span><path></span> <span><groupId></span>org.mapstruct<span></groupId></span> <span><artifactId></span>mapstruct-processor<span></artifactId></span> <span><version></span>${org.mapstruct.version}<span></version></span> <span></path></span> <span><!-- Otras dependencias de procesadores de anotaciones --></span> <span></annotationProcessorPaths></span> <span></configuration></span> <span></plugin></span> <span></plugins></span> <span></build></span><properties> <org.mapstruct.version>1.6.3</org.mapstruct.version> </properties> <dependencies> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>${org.mapstruct.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <!-- Dependiendo de tu proyecto --> <target>1.8</target> <!-- Dependiendo de tu proyecto --> <annotationProcessorPaths> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> </path> <!-- Otras dependencias de procesadores de anotaciones --> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build>
Enter fullscreen mode Exit fullscreen mode
En caso de utilizar otra herramienta para la construcción de proyectos, se puede consultar la documentación oficial.
Configurar con Lombok
MapStruct se puede combinar con Lombok para reducir la cantidad de código que se escribe. Para ello, es necesario agregar las dependencias de lombok
y lombok-mapstruct-binding
, esta última es necesaria para que MapStruct pueda reconocer las anotaciones de Lombok.
<span><dependency></span><span><groupId></span>org.projectlombok<span></groupId></span><span><artifactId></span>lombok<span></artifactId></span><span><version></span>1.18.36<span></version></span><span><scope></span>provided<span></scope></span><span></dependency></span><span><dependency></span><span><groupId></span>org.projectlombok<span></groupId></span><span><artifactId></span>lombok-mapstruct-binding<span></artifactId></span><span><version></span>0.2.0<span></version></span><span><scope></span>provided<span></scope></span><span></dependency></span><span><dependency></span> <span><groupId></span>org.projectlombok<span></groupId></span> <span><artifactId></span>lombok<span></artifactId></span> <span><version></span>1.18.36<span></version></span> <span><scope></span>provided<span></scope></span> <span></dependency></span> <span><dependency></span> <span><groupId></span>org.projectlombok<span></groupId></span> <span><artifactId></span>lombok-mapstruct-binding<span></artifactId></span> <span><version></span>0.2.0<span></version></span> <span><scope></span>provided<span></scope></span> <span></dependency></span><dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.36</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok-mapstruct-binding</artifactId> <version>0.2.0</version> <scope>provided</scope> </dependency>
Enter fullscreen mode Exit fullscreen mode
Ahora en el apartado de annotationProcessorPaths
del maven-compiler-plugin
se deben configurar estas dependencias.
<span><path></span><span><groupId></span>org.projectlombok<span></groupId></span><span><artifactId></span>lombok<span></artifactId></span><span><version></span>1.18.36<span></version></span><span></path></span><span><path></span><span><groupId></span>org.projectlombok<span></groupId></span><span><artifactId></span>lombok-mapstruct-binding<span></artifactId></span><span><version></span>0.2.0<span></version></span><span></path></span><span><path></span> <span><groupId></span>org.projectlombok<span></groupId></span> <span><artifactId></span>lombok<span></artifactId></span> <span><version></span>1.18.36<span></version></span> <span></path></span> <span><path></span> <span><groupId></span>org.projectlombok<span></groupId></span> <span><artifactId></span>lombok-mapstruct-binding<span></artifactId></span> <span><version></span>0.2.0<span></version></span> <span></path></span><path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.36</version> </path> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok-mapstruct-binding</artifactId> <version>0.2.0</version> </path>
Enter fullscreen mode Exit fullscreen mode
Al final, nuestro archivo pom.xml
queda de la siguiente manera.
<span><?xml version="1.0" encoding="UTF-8"?></span><span><project</span> <span>xmlns=</span><span>"http://maven.apache.org/POM/4.0.0"</span><span>xmlns:xsi=</span><span>"http://www.w3.org/2001/XMLSchema-instance"</span><span>xsi:schemaLocation=</span><span>"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span><span>></span><span><modelVersion></span>4.0.0<span></modelVersion></span><span><groupId></span>dev.asjordi<span></groupId></span><span><artifactId></span>MapStruct<span></artifactId></span><span><version></span>1.0-SNAPSHOT<span></version></span><span><properties></span><span><maven.compiler.source></span>21<span></maven.compiler.source></span><span><maven.compiler.target></span>21<span></maven.compiler.target></span><span><project.build.sourceEncoding></span>UTF-8<span></project.build.sourceEncoding></span><span><org.mapstruct.version></span>1.6.3<span></org.mapstruct.version></span><span></properties></span><span><dependencies></span><span><dependency></span><span><groupId></span>org.modelmapper<span></groupId></span><span><artifactId></span>modelmapper<span></artifactId></span><span><version></span>3.2.0<span></version></span><span></dependency></span><span><dependency></span><span><groupId></span>org.mapstruct<span></groupId></span><span><artifactId></span>mapstruct<span></artifactId></span><span><version></span>${org.mapstruct.version}<span></version></span><span></dependency></span><span><dependency></span><span><groupId></span>org.projectlombok<span></groupId></span><span><artifactId></span>lombok<span></artifactId></span><span><version></span>1.18.36<span></version></span><span><scope></span>provided<span></scope></span><span></dependency></span><span><dependency></span><span><groupId></span>org.projectlombok<span></groupId></span><span><artifactId></span>lombok-mapstruct-binding<span></artifactId></span><span><version></span>0.2.0<span></version></span><span><scope></span>provided<span></scope></span><span></dependency></span><span></dependencies></span><span><build></span><span><plugins></span><span><plugin></span><span><groupId></span>org.apache.maven.plugins<span></groupId></span><span><artifactId></span>maven-compiler-plugin<span></artifactId></span><span><version></span>3.8.1<span></version></span><span><configuration></span><span><source></span>21<span></source></span><span><target></span>21<span></target></span><span><annotationProcessorPaths></span><span><path></span><span><groupId></span>org.mapstruct<span></groupId></span><span><artifactId></span>mapstruct-processor<span></artifactId></span><span><version></span>${org.mapstruct.version}<span></version></span><span></path></span><span><path></span><span><groupId></span>org.projectlombok<span></groupId></span><span><artifactId></span>lombok<span></artifactId></span><span><version></span>1.18.36<span></version></span><span></path></span><span><path></span><span><groupId></span>org.projectlombok<span></groupId></span><span><artifactId></span>lombok-mapstruct-binding<span></artifactId></span><span><version></span>0.2.0<span></version></span><span></path></span><span></annotationProcessorPaths></span><span></configuration></span><span></plugin></span><span><plugin></span><span><groupId></span>org.apache.maven.plugins<span></groupId></span><span><artifactId></span>maven-surefire-plugin<span></artifactId></span><span><version></span>3.2.5<span></version></span><span></plugin></span><span></plugins></span><span></build></span><span></project></span><span><?xml version="1.0" encoding="UTF-8"?></span> <span><project</span> <span>xmlns=</span><span>"http://maven.apache.org/POM/4.0.0"</span> <span>xmlns:xsi=</span><span>"http://www.w3.org/2001/XMLSchema-instance"</span> <span>xsi:schemaLocation=</span><span>"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span><span>></span> <span><modelVersion></span>4.0.0<span></modelVersion></span> <span><groupId></span>dev.asjordi<span></groupId></span> <span><artifactId></span>MapStruct<span></artifactId></span> <span><version></span>1.0-SNAPSHOT<span></version></span> <span><properties></span> <span><maven.compiler.source></span>21<span></maven.compiler.source></span> <span><maven.compiler.target></span>21<span></maven.compiler.target></span> <span><project.build.sourceEncoding></span>UTF-8<span></project.build.sourceEncoding></span> <span><org.mapstruct.version></span>1.6.3<span></org.mapstruct.version></span> <span></properties></span> <span><dependencies></span> <span><dependency></span> <span><groupId></span>org.modelmapper<span></groupId></span> <span><artifactId></span>modelmapper<span></artifactId></span> <span><version></span>3.2.0<span></version></span> <span></dependency></span> <span><dependency></span> <span><groupId></span>org.mapstruct<span></groupId></span> <span><artifactId></span>mapstruct<span></artifactId></span> <span><version></span>${org.mapstruct.version}<span></version></span> <span></dependency></span> <span><dependency></span> <span><groupId></span>org.projectlombok<span></groupId></span> <span><artifactId></span>lombok<span></artifactId></span> <span><version></span>1.18.36<span></version></span> <span><scope></span>provided<span></scope></span> <span></dependency></span> <span><dependency></span> <span><groupId></span>org.projectlombok<span></groupId></span> <span><artifactId></span>lombok-mapstruct-binding<span></artifactId></span> <span><version></span>0.2.0<span></version></span> <span><scope></span>provided<span></scope></span> <span></dependency></span> <span></dependencies></span> <span><build></span> <span><plugins></span> <span><plugin></span> <span><groupId></span>org.apache.maven.plugins<span></groupId></span> <span><artifactId></span>maven-compiler-plugin<span></artifactId></span> <span><version></span>3.8.1<span></version></span> <span><configuration></span> <span><source></span>21<span></source></span> <span><target></span>21<span></target></span> <span><annotationProcessorPaths></span> <span><path></span> <span><groupId></span>org.mapstruct<span></groupId></span> <span><artifactId></span>mapstruct-processor<span></artifactId></span> <span><version></span>${org.mapstruct.version}<span></version></span> <span></path></span> <span><path></span> <span><groupId></span>org.projectlombok<span></groupId></span> <span><artifactId></span>lombok<span></artifactId></span> <span><version></span>1.18.36<span></version></span> <span></path></span> <span><path></span> <span><groupId></span>org.projectlombok<span></groupId></span> <span><artifactId></span>lombok-mapstruct-binding<span></artifactId></span> <span><version></span>0.2.0<span></version></span> <span></path></span> <span></annotationProcessorPaths></span> <span></configuration></span> <span></plugin></span> <span><plugin></span> <span><groupId></span>org.apache.maven.plugins<span></groupId></span> <span><artifactId></span>maven-surefire-plugin<span></artifactId></span> <span><version></span>3.2.5<span></version></span> <span></plugin></span> <span></plugins></span> <span></build></span> <span></project></span><?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>dev.asjordi</groupId> <artifactId>MapStruct</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>21</maven.compiler.source> <maven.compiler.target>21</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <org.mapstruct.version>1.6.3</org.mapstruct.version> </properties> <dependencies> <dependency> <groupId>org.modelmapper</groupId> <artifactId>modelmapper</artifactId> <version>3.2.0</version> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>${org.mapstruct.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.36</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok-mapstruct-binding</artifactId> <version>0.2.0</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>21</source> <target>21</target> <annotationProcessorPaths> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> </path> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.36</version> </path> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok-mapstruct-binding</artifactId> <version>0.2.0</version> </path> </annotationProcessorPaths> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.2.5</version> </plugin> </plugins> </build> </project>
Enter fullscreen mode Exit fullscreen mode
Configurar JUnit
Para realizar pruebas unitarias y verificar que los mapeos se están realizando correctamente, es necesario agregar la dependencia de JUnit al archivo pom.xml
(este paso es completamente opcional).
<span><dependency></span><span><groupId></span>org.junit.jupiter<span></groupId></span><span><artifactId></span>junit-jupiter-api<span></artifactId></span><span><version></span>5.11.3<span></version></span><span><scope></span>test<span></scope></span><span></dependency></span><span><dependency></span><span><groupId></span>org.junit<span></groupId></span><span><artifactId></span>junit-bom<span></artifactId></span><span><version></span>5.11.3<span></version></span><span><type></span>pom<span></type></span><span><scope></span>test<span></scope></span><span></dependency></span><span><dependency></span> <span><groupId></span>org.junit.jupiter<span></groupId></span> <span><artifactId></span>junit-jupiter-api<span></artifactId></span> <span><version></span>5.11.3<span></version></span> <span><scope></span>test<span></scope></span> <span></dependency></span> <span><dependency></span> <span><groupId></span>org.junit<span></groupId></span> <span><artifactId></span>junit-bom<span></artifactId></span> <span><version></span>5.11.3<span></version></span> <span><type></span>pom<span></type></span> <span><scope></span>test<span></scope></span> <span></dependency></span><dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.11.3</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit</groupId> <artifactId>junit-bom</artifactId> <version>5.11.3</version> <type>pom</type> <scope>test</scope> </dependency>
Enter fullscreen mode Exit fullscreen mode
En el apartado de build
se debe agregar el plugin maven-surefire-plugin
para ejecutar las pruebas unitarias.
<span><plugin></span><span><groupId></span>org.apache.maven.plugins<span></groupId></span><span><artifactId></span>maven-surefire-plugin<span></artifactId></span><span><version></span>3.2.5<span></version></span><span></plugin></span><span><plugin></span> <span><groupId></span>org.apache.maven.plugins<span></groupId></span> <span><artifactId></span>maven-surefire-plugin<span></artifactId></span> <span><version></span>3.2.5<span></version></span> <span></plugin></span><plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.2.5</version> </plugin>
Enter fullscreen mode Exit fullscreen mode
Crear clases de ejemplo
Supongamos que tenemos una clase User
con los atributos id
, name
y email
que nos servirá como entidad base para mapearla a un DTO (Data Transfer Object).
<span>import</span> <span>lombok.*</span><span>;</span><span>@Data</span><span>@NoArgsConstructor</span><span>@AllArgsConstructor</span><span>public</span> <span>class</span> <span>User</span> <span>{</span><span>private</span> <span>Long</span> <span>id</span><span>;</span><span>private</span> <span>String</span> <span>name</span><span>;</span><span>private</span> <span>String</span> <span>email</span><span>;</span><span>}</span><span>import</span> <span>lombok.*</span><span>;</span> <span>@Data</span> <span>@NoArgsConstructor</span> <span>@AllArgsConstructor</span> <span>public</span> <span>class</span> <span>User</span> <span>{</span> <span>private</span> <span>Long</span> <span>id</span><span>;</span> <span>private</span> <span>String</span> <span>name</span><span>;</span> <span>private</span> <span>String</span> <span>email</span><span>;</span> <span>}</span>import lombok.*; @Data @NoArgsConstructor @AllArgsConstructor public class User { private Long id; private String name; private String email; }
Enter fullscreen mode Exit fullscreen mode
Ahora definimos una clase UserDTO
con los atributos name
y email
.
<span>import</span> <span>lombok.*</span><span>;</span><span>@Data</span><span>@NoArgsConstructor</span><span>@AllArgsConstructor</span><span>public</span> <span>class</span> <span>UserDTO</span> <span>{</span><span>private</span> <span>String</span> <span>name</span><span>;</span><span>private</span> <span>String</span> <span>email</span><span>;</span><span>}</span><span>import</span> <span>lombok.*</span><span>;</span> <span>@Data</span> <span>@NoArgsConstructor</span> <span>@AllArgsConstructor</span> <span>public</span> <span>class</span> <span>UserDTO</span> <span>{</span> <span>private</span> <span>String</span> <span>name</span><span>;</span> <span>private</span> <span>String</span> <span>email</span><span>;</span> <span>}</span>import lombok.*; @Data @NoArgsConstructor @AllArgsConstructor public class UserDTO { private String name; private String email; }
Enter fullscreen mode Exit fullscreen mode
MapStruct también funciona con records, en general, este puede ser un mejor enfoque dado que la principal función de un DTO es almacenar y transferir datos de un punto a otro, donde la inmutabilidad es una característica deseable. Creamos un record UserRecord
con los atributos name
y email
, en este caso no es necesario utilizar Lombok.
Si tienes dudas sobre los records, puedes consultar dentro del blog al respecto.
<span>public</span> <span>record</span> <span>UserRecord</span><span>(</span><span>String</span> <span>name</span><span>,</span> <span>String</span> <span>email</span><span>)</span> <span>{</span> <span>}</span><span>public</span> <span>record</span> <span>UserRecord</span><span>(</span><span>String</span> <span>name</span><span>,</span> <span>String</span> <span>email</span><span>)</span> <span>{</span> <span>}</span>public record UserRecord(String name, String email) { }
Enter fullscreen mode Exit fullscreen mode
Crear un mapper
Para crear un mapper con MapStruct, se debe crear una interfaz con el nombre UserMapper
(el nombre depende de cada uno) y anotarla con @Mapper
. En esta interfaz se deben definir los métodos de mapeo entre las clases User
, UserDTO
y UserRecord
.
<span>import</span> <span>org.mapstruct.Mapper</span><span>;</span><span>import</span> <span>org.mapstruct.Mapping</span><span>;</span><span>import</span> <span>org.mapstruct.factory.Mappers</span><span>;</span><span>@Mapper</span><span>public</span> <span>interface</span> <span>UserMapper</span> <span>{</span><span>UserMapper</span> <span>INSTANCE</span> <span>=</span> <span>Mappers</span><span>.</span><span>getMapper</span><span>(</span><span>UserMapper</span><span>.</span><span>class</span><span>);</span><span>UserDTO</span> <span>userToUserDTO</span><span>(</span><span>User</span> <span>user</span><span>);</span><span>UserRecord</span> <span>userToUserRecord</span><span>(</span><span>User</span> <span>user</span><span>);</span><span>User</span> <span>userDTOToUser</span><span>(</span><span>UserDTO</span> <span>userDTO</span><span>);</span><span>User</span> <span>userRecordToUser</span><span>(</span><span>UserRecord</span> <span>userRecord</span><span>);</span><span>}</span><span>import</span> <span>org.mapstruct.Mapper</span><span>;</span> <span>import</span> <span>org.mapstruct.Mapping</span><span>;</span> <span>import</span> <span>org.mapstruct.factory.Mappers</span><span>;</span> <span>@Mapper</span> <span>public</span> <span>interface</span> <span>UserMapper</span> <span>{</span> <span>UserMapper</span> <span>INSTANCE</span> <span>=</span> <span>Mappers</span><span>.</span><span>getMapper</span><span>(</span><span>UserMapper</span><span>.</span><span>class</span><span>);</span> <span>UserDTO</span> <span>userToUserDTO</span><span>(</span><span>User</span> <span>user</span><span>);</span> <span>UserRecord</span> <span>userToUserRecord</span><span>(</span><span>User</span> <span>user</span><span>);</span> <span>User</span> <span>userDTOToUser</span><span>(</span><span>UserDTO</span> <span>userDTO</span><span>);</span> <span>User</span> <span>userRecordToUser</span><span>(</span><span>UserRecord</span> <span>userRecord</span><span>);</span> <span>}</span>import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; @Mapper public interface UserMapper { UserMapper INSTANCE = Mappers.getMapper(UserMapper.class); UserDTO userToUserDTO(User user); UserRecord userToUserRecord(User user); User userDTOToUser(UserDTO userDTO); User userRecordToUser(UserRecord userRecord); }
Enter fullscreen mode Exit fullscreen mode
La interfaz UserMapper
tiene las siguientes características:
- La anotación
@Mapper
indica que esta interfaz es un mapper. - La constante
INSTANCE
es un objeto que se utiliza para obtener una instancia del mapper. - Los métodos
userToUserDTO
yuserToUserRecord
mapean un objetoUser
a un objetoUserDTO
yUserRecord
respectivamente. - Los métodos
userDTOToUser
yuserRecordToUser
mapean un objetoUserDTO
yUserRecord
a un objetoUser
respectivamente.
Si tenemos atributos con diferente nombre, tanto en la entidad (source) como en el DTO (target), se puede utilizar la anotación @Mapping
para indicar el nombre del atributo en la entidad y el nombre del atributo en el DTO.
<span>@Mapping</span><span>(</span><span>source</span> <span>=</span> <span>"name"</span><span>,</span> <span>target</span> <span>=</span> <span>"name"</span><span>)</span><span>@Mapping</span><span>(</span><span>source</span> <span>=</span> <span>"email"</span><span>,</span> <span>target</span> <span>=</span> <span>"email"</span><span>)</span><span>UserDTO</span> <span>userToUserDTO</span><span>(</span><span>User</span> <span>user</span><span>);</span><span>@Mapping</span><span>(</span><span>source</span> <span>=</span> <span>"name"</span><span>,</span> <span>target</span> <span>=</span> <span>"name"</span><span>)</span> <span>@Mapping</span><span>(</span><span>source</span> <span>=</span> <span>"email"</span><span>,</span> <span>target</span> <span>=</span> <span>"email"</span><span>)</span> <span>UserDTO</span> <span>userToUserDTO</span><span>(</span><span>User</span> <span>user</span><span>);</span>@Mapping(source = "name", target = "name") @Mapping(source = "email", target = "email") UserDTO userToUserDTO(User user);
Enter fullscreen mode Exit fullscreen mode
En este punto ya tenemos tanto la entidad a mapear como el DTO, así como el mapper que se encargará de realizar el mapeo entre ambos.
Realizar el mapeo
Para realizar el mapeo de una entidad, basta con utilizar el atributo INSTANCE
de la interfaz UserMapper
y llamar al método correspondiente, tanto para mapear de la entidad al DTO como del DTO a la entidad.
<span>public</span> <span>class</span> <span>Main</span> <span>{</span><span>public</span> <span>static</span> <span>void</span> <span>main</span><span>(</span><span>String</span><span>[]</span> <span>args</span><span>)</span> <span>{</span><span>// Mapeo de User a UserDTO y UserRecord</span><span>User</span> <span>user</span> <span>=</span> <span>new</span> <span>User</span><span>(</span><span>1L</span><span>,</span> <span>"John Doe"</span><span>,</span> <span>"jonh@gmail.com"</span><span>);</span><span>UserDTO</span> <span>userDTO</span> <span>=</span> <span>UserMapper</span><span>.</span><span>INSTANCE</span><span>.</span><span>userToUserDTO</span><span>(</span><span>user</span><span>);</span><span>UserRecord</span> <span>userRecord</span> <span>=</span> <span>UserMapper</span><span>.</span><span>INSTANCE</span><span>.</span><span>userToUserRecord</span><span>(</span><span>user</span><span>);</span><span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"User: "</span> <span>+</span> <span>user</span><span>);</span> <span>// User: User(id=1, name=John Doe, email=jonh@gmail.com)</span><span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"UserDTO: "</span> <span>+</span> <span>userDTO</span><span>);</span> <span>// UserDTO: UserDTO(name=John Doe, email=jonh@gmail.com)</span><span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"UserRecord: "</span> <span>+</span> <span>userRecord</span><span>);</span> <span>// UserRecord: UserRecord[name=John Doe, email=jonh@gmail.com]</span><span>// Mapeo inverso de UserDTO a User y UserRecord a User</span><span>User</span> <span>userFromDTO</span> <span>=</span> <span>UserMapper</span><span>.</span><span>INSTANCE</span><span>.</span><span>userDTOToUser</span><span>(</span><span>userDTO</span><span>);</span><span>User</span> <span>userFromRecord</span> <span>=</span> <span>UserMapper</span><span>.</span><span>INSTANCE</span><span>.</span><span>userRecordToUser</span><span>(</span><span>userRecord</span><span>);</span><span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"User from DTO: "</span> <span>+</span> <span>userFromDTO</span><span>);</span> <span>// User from DTO: User(id=null, name=John Doe, email=jonh@gmail.com)</span><span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"User from Record: "</span> <span>+</span> <span>userFromRecord</span><span>);</span> <span>// User from Record: User(id=null, name=John Doe, email=jonh@gmail.com)</span><span>}</span><span>}</span><span>public</span> <span>class</span> <span>Main</span> <span>{</span> <span>public</span> <span>static</span> <span>void</span> <span>main</span><span>(</span><span>String</span><span>[]</span> <span>args</span><span>)</span> <span>{</span> <span>// Mapeo de User a UserDTO y UserRecord</span> <span>User</span> <span>user</span> <span>=</span> <span>new</span> <span>User</span><span>(</span><span>1L</span><span>,</span> <span>"John Doe"</span><span>,</span> <span>"jonh@gmail.com"</span><span>);</span> <span>UserDTO</span> <span>userDTO</span> <span>=</span> <span>UserMapper</span><span>.</span><span>INSTANCE</span><span>.</span><span>userToUserDTO</span><span>(</span><span>user</span><span>);</span> <span>UserRecord</span> <span>userRecord</span> <span>=</span> <span>UserMapper</span><span>.</span><span>INSTANCE</span><span>.</span><span>userToUserRecord</span><span>(</span><span>user</span><span>);</span> <span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"User: "</span> <span>+</span> <span>user</span><span>);</span> <span>// User: User(id=1, name=John Doe, email=jonh@gmail.com)</span> <span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"UserDTO: "</span> <span>+</span> <span>userDTO</span><span>);</span> <span>// UserDTO: UserDTO(name=John Doe, email=jonh@gmail.com)</span> <span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"UserRecord: "</span> <span>+</span> <span>userRecord</span><span>);</span> <span>// UserRecord: UserRecord[name=John Doe, email=jonh@gmail.com]</span> <span>// Mapeo inverso de UserDTO a User y UserRecord a User</span> <span>User</span> <span>userFromDTO</span> <span>=</span> <span>UserMapper</span><span>.</span><span>INSTANCE</span><span>.</span><span>userDTOToUser</span><span>(</span><span>userDTO</span><span>);</span> <span>User</span> <span>userFromRecord</span> <span>=</span> <span>UserMapper</span><span>.</span><span>INSTANCE</span><span>.</span><span>userRecordToUser</span><span>(</span><span>userRecord</span><span>);</span> <span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"User from DTO: "</span> <span>+</span> <span>userFromDTO</span><span>);</span> <span>// User from DTO: User(id=null, name=John Doe, email=jonh@gmail.com)</span> <span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"User from Record: "</span> <span>+</span> <span>userFromRecord</span><span>);</span> <span>// User from Record: User(id=null, name=John Doe, email=jonh@gmail.com)</span> <span>}</span> <span>}</span>public class Main { public static void main(String[] args) { // Mapeo de User a UserDTO y UserRecord User user = new User(1L, "John Doe", "jonh@gmail.com"); UserDTO userDTO = UserMapper.INSTANCE.userToUserDTO(user); UserRecord userRecord = UserMapper.INSTANCE.userToUserRecord(user); System.out.println("User: " + user); // User: User(id=1, name=John Doe, email=jonh@gmail.com) System.out.println("UserDTO: " + userDTO); // UserDTO: UserDTO(name=John Doe, email=jonh@gmail.com) System.out.println("UserRecord: " + userRecord); // UserRecord: UserRecord[name=John Doe, email=jonh@gmail.com] // Mapeo inverso de UserDTO a User y UserRecord a User User userFromDTO = UserMapper.INSTANCE.userDTOToUser(userDTO); User userFromRecord = UserMapper.INSTANCE.userRecordToUser(userRecord); System.out.println("User from DTO: " + userFromDTO); // User from DTO: User(id=null, name=John Doe, email=jonh@gmail.com) System.out.println("User from Record: " + userFromRecord); // User from Record: User(id=null, name=John Doe, email=jonh@gmail.com) } }
Enter fullscreen mode Exit fullscreen mode
En el ejemplo anterior, se crea un objeto User
con los atributos id
, name
y email
, luego se mapea a un objeto UserDTO
y UserRecord
. Posteriormente, se realiza el mapeo inverso de un objeto UserDTO
y UserRecord
a un objeto User
.
Test unitarios
Para verificar que los mapeos entre objetos se están realizando correctamente, se pueden realizar pruebas unitarias con JUnit. En este caso, se crea una clase MapstructTest
con diferentes pruebas unitarias para verificar los mapeos entre objetos.
<span>import</span> <span>org.junit.jupiter.api.BeforeAll</span><span>;</span><span>import</span> <span>org.junit.jupiter.api.DisplayName</span><span>;</span><span>import</span> <span>org.junit.jupiter.api.Test</span><span>;</span><span>import</span> <span>static</span> <span>org</span><span>.</span><span>junit</span><span>.</span><span>jupiter</span><span>.</span><span>api</span><span>.</span><span>Assertions</span><span>.</span><span>assertEquals</span><span>;</span><span>public</span> <span>class</span> <span>MapstructTest</span> <span>{</span><span>private</span> <span>static</span> <span>User</span> <span>user</span><span>;</span><span>@BeforeAll</span><span>public</span> <span>static</span> <span>void</span> <span>setup</span><span>()</span> <span>{</span><span>user</span> <span>=</span> <span>new</span> <span>User</span><span>(</span><span>1L</span><span>,</span> <span>"John Doe"</span><span>,</span> <span>"jonh@gmail.com"</span><span>);</span><span>}</span><span>@Test</span><span>@DisplayName</span><span>(</span><span>"Test User to UserDTO"</span><span>)</span><span>void</span> <span>testUserToUserDTO</span><span>()</span> <span>{</span><span>UserDTO</span> <span>userDTO</span> <span>=</span> <span>UserMapper</span><span>.</span><span>INSTANCE</span><span>.</span><span>userToUserDTO</span><span>(</span><span>user</span><span>);</span><span>assertEquals</span><span>(</span><span>user</span><span>.</span><span>getName</span><span>(),</span> <span>userDTO</span><span>.</span><span>getName</span><span>());</span><span>assertEquals</span><span>(</span><span>user</span><span>.</span><span>getEmail</span><span>(),</span> <span>userDTO</span><span>.</span><span>getEmail</span><span>());</span><span>}</span><span>@Test</span><span>@DisplayName</span><span>(</span><span>"Test User to UserRecord"</span><span>)</span><span>void</span> <span>testUserToUserRecord</span><span>()</span> <span>{</span><span>UserRecord</span> <span>userRecord</span> <span>=</span> <span>UserMapper</span><span>.</span><span>INSTANCE</span><span>.</span><span>userToUserRecord</span><span>(</span><span>user</span><span>);</span><span>assertEquals</span><span>(</span><span>user</span><span>.</span><span>getName</span><span>(),</span> <span>userRecord</span><span>.</span><span>name</span><span>());</span><span>assertEquals</span><span>(</span><span>user</span><span>.</span><span>getEmail</span><span>(),</span> <span>userRecord</span><span>.</span><span>email</span><span>());</span><span>}</span><span>@Test</span><span>@DisplayName</span><span>(</span><span>"Test UserDTO to User"</span><span>)</span><span>void</span> <span>testUserDTOToUser</span><span>()</span> <span>{</span><span>UserDTO</span> <span>userDTO</span> <span>=</span> <span>UserMapper</span><span>.</span><span>INSTANCE</span><span>.</span><span>userToUserDTO</span><span>(</span><span>user</span><span>);</span><span>User</span> <span>userFromDTO</span> <span>=</span> <span>UserMapper</span><span>.</span><span>INSTANCE</span><span>.</span><span>userDTOToUser</span><span>(</span><span>userDTO</span><span>);</span><span>assertEquals</span><span>(</span><span>userDTO</span><span>.</span><span>getName</span><span>(),</span> <span>userFromDTO</span><span>.</span><span>getName</span><span>());</span><span>assertEquals</span><span>(</span><span>userDTO</span><span>.</span><span>getEmail</span><span>(),</span> <span>userFromDTO</span><span>.</span><span>getEmail</span><span>());</span><span>}</span><span>@Test</span><span>@DisplayName</span><span>(</span><span>"Test UserRecord to User"</span><span>)</span><span>void</span> <span>testUserRecordToUser</span><span>()</span> <span>{</span><span>UserRecord</span> <span>userRecord</span> <span>=</span> <span>UserMapper</span><span>.</span><span>INSTANCE</span><span>.</span><span>userToUserRecord</span><span>(</span><span>user</span><span>);</span><span>User</span> <span>userFromRecord</span> <span>=</span> <span>UserMapper</span><span>.</span><span>INSTANCE</span><span>.</span><span>userRecordToUser</span><span>(</span><span>userRecord</span><span>);</span><span>assertEquals</span><span>(</span><span>userRecord</span><span>.</span><span>name</span><span>(),</span> <span>userFromRecord</span><span>.</span><span>getName</span><span>());</span><span>assertEquals</span><span>(</span><span>userRecord</span><span>.</span><span>email</span><span>(),</span> <span>userFromRecord</span><span>.</span><span>getEmail</span><span>());</span><span>}</span><span>}</span><span>import</span> <span>org.junit.jupiter.api.BeforeAll</span><span>;</span> <span>import</span> <span>org.junit.jupiter.api.DisplayName</span><span>;</span> <span>import</span> <span>org.junit.jupiter.api.Test</span><span>;</span> <span>import</span> <span>static</span> <span>org</span><span>.</span><span>junit</span><span>.</span><span>jupiter</span><span>.</span><span>api</span><span>.</span><span>Assertions</span><span>.</span><span>assertEquals</span><span>;</span> <span>public</span> <span>class</span> <span>MapstructTest</span> <span>{</span> <span>private</span> <span>static</span> <span>User</span> <span>user</span><span>;</span> <span>@BeforeAll</span> <span>public</span> <span>static</span> <span>void</span> <span>setup</span><span>()</span> <span>{</span> <span>user</span> <span>=</span> <span>new</span> <span>User</span><span>(</span><span>1L</span><span>,</span> <span>"John Doe"</span><span>,</span> <span>"jonh@gmail.com"</span><span>);</span> <span>}</span> <span>@Test</span> <span>@DisplayName</span><span>(</span><span>"Test User to UserDTO"</span><span>)</span> <span>void</span> <span>testUserToUserDTO</span><span>()</span> <span>{</span> <span>UserDTO</span> <span>userDTO</span> <span>=</span> <span>UserMapper</span><span>.</span><span>INSTANCE</span><span>.</span><span>userToUserDTO</span><span>(</span><span>user</span><span>);</span> <span>assertEquals</span><span>(</span><span>user</span><span>.</span><span>getName</span><span>(),</span> <span>userDTO</span><span>.</span><span>getName</span><span>());</span> <span>assertEquals</span><span>(</span><span>user</span><span>.</span><span>getEmail</span><span>(),</span> <span>userDTO</span><span>.</span><span>getEmail</span><span>());</span> <span>}</span> <span>@Test</span> <span>@DisplayName</span><span>(</span><span>"Test User to UserRecord"</span><span>)</span> <span>void</span> <span>testUserToUserRecord</span><span>()</span> <span>{</span> <span>UserRecord</span> <span>userRecord</span> <span>=</span> <span>UserMapper</span><span>.</span><span>INSTANCE</span><span>.</span><span>userToUserRecord</span><span>(</span><span>user</span><span>);</span> <span>assertEquals</span><span>(</span><span>user</span><span>.</span><span>getName</span><span>(),</span> <span>userRecord</span><span>.</span><span>name</span><span>());</span> <span>assertEquals</span><span>(</span><span>user</span><span>.</span><span>getEmail</span><span>(),</span> <span>userRecord</span><span>.</span><span>email</span><span>());</span> <span>}</span> <span>@Test</span> <span>@DisplayName</span><span>(</span><span>"Test UserDTO to User"</span><span>)</span> <span>void</span> <span>testUserDTOToUser</span><span>()</span> <span>{</span> <span>UserDTO</span> <span>userDTO</span> <span>=</span> <span>UserMapper</span><span>.</span><span>INSTANCE</span><span>.</span><span>userToUserDTO</span><span>(</span><span>user</span><span>);</span> <span>User</span> <span>userFromDTO</span> <span>=</span> <span>UserMapper</span><span>.</span><span>INSTANCE</span><span>.</span><span>userDTOToUser</span><span>(</span><span>userDTO</span><span>);</span> <span>assertEquals</span><span>(</span><span>userDTO</span><span>.</span><span>getName</span><span>(),</span> <span>userFromDTO</span><span>.</span><span>getName</span><span>());</span> <span>assertEquals</span><span>(</span><span>userDTO</span><span>.</span><span>getEmail</span><span>(),</span> <span>userFromDTO</span><span>.</span><span>getEmail</span><span>());</span> <span>}</span> <span>@Test</span> <span>@DisplayName</span><span>(</span><span>"Test UserRecord to User"</span><span>)</span> <span>void</span> <span>testUserRecordToUser</span><span>()</span> <span>{</span> <span>UserRecord</span> <span>userRecord</span> <span>=</span> <span>UserMapper</span><span>.</span><span>INSTANCE</span><span>.</span><span>userToUserRecord</span><span>(</span><span>user</span><span>);</span> <span>User</span> <span>userFromRecord</span> <span>=</span> <span>UserMapper</span><span>.</span><span>INSTANCE</span><span>.</span><span>userRecordToUser</span><span>(</span><span>userRecord</span><span>);</span> <span>assertEquals</span><span>(</span><span>userRecord</span><span>.</span><span>name</span><span>(),</span> <span>userFromRecord</span><span>.</span><span>getName</span><span>());</span> <span>assertEquals</span><span>(</span><span>userRecord</span><span>.</span><span>email</span><span>(),</span> <span>userFromRecord</span><span>.</span><span>getEmail</span><span>());</span> <span>}</span> <span>}</span>import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class MapstructTest { private static User user; @BeforeAll public static void setup() { user = new User(1L, "John Doe", "jonh@gmail.com"); } @Test @DisplayName("Test User to UserDTO") void testUserToUserDTO() { UserDTO userDTO = UserMapper.INSTANCE.userToUserDTO(user); assertEquals(user.getName(), userDTO.getName()); assertEquals(user.getEmail(), userDTO.getEmail()); } @Test @DisplayName("Test User to UserRecord") void testUserToUserRecord() { UserRecord userRecord = UserMapper.INSTANCE.userToUserRecord(user); assertEquals(user.getName(), userRecord.name()); assertEquals(user.getEmail(), userRecord.email()); } @Test @DisplayName("Test UserDTO to User") void testUserDTOToUser() { UserDTO userDTO = UserMapper.INSTANCE.userToUserDTO(user); User userFromDTO = UserMapper.INSTANCE.userDTOToUser(userDTO); assertEquals(userDTO.getName(), userFromDTO.getName()); assertEquals(userDTO.getEmail(), userFromDTO.getEmail()); } @Test @DisplayName("Test UserRecord to User") void testUserRecordToUser() { UserRecord userRecord = UserMapper.INSTANCE.userToUserRecord(user); User userFromRecord = UserMapper.INSTANCE.userRecordToUser(userRecord); assertEquals(userRecord.name(), userFromRecord.getName()); assertEquals(userRecord.email(), userFromRecord.getEmail()); } }
Enter fullscreen mode Exit fullscreen mode
Se pueden ejecutar los tests desde el propio IDE o usando el comando mvn test
desde la línea de comandos.
Conclusiones
Como hemos visto crear mapeos entre objetos es muy sencillo utilizando MapStruct. Además, al ejecutarse en tiempo de compilación proporciona un mejor rendimiento y una comprobación de errores más rápida. En este ejemplo hemos visto cómo mapear una entidad a un DTO y viceversa, así como realizar pruebas unitarias para verificar que los mapeos se están realizando correctamente, puede que los mapeos del ejemplo sean simples, pero funciona de la misma manera con mapeos más complejos.
Puedes consultar el código fuente de este ejemplo en mi repositorio de GitHub.
暂无评论内容