Mapeo de Beans con MapStruct


En este tutorial se explica de forma simple como utilizar MapStruct para realizar el mapeo de beans de forma automática en tiempo de compilación sin uso del Java reflection API.

1. Para configurar Map struct se debe incluir la siguiente dependencia en el archivo pom.xml.

<properties>
	<org.mapstruct.version>1.1.0.Beta2</org.mapstruct.version>
</properties>
<dependencies>
	<dependency>
		<groupId>org.mapstruct</groupId>
		<artifactId>mapstruct-jdk8</artifactId>
		<version>${org.mapstruct.version}</version>
	</dependency>
</dependencies>

MapStruct no utiliza reflection para hacer el mapeo de los beans en tiempo de ejecución, sino que en tiempo de compilación utiliza el goal generate-sources de Maven para generar las clases necesarias de forma automática, para hacer esto se debe incluir el siguiente plugin en el archivo pom.xml:

<build>
	<plugins>
		<plugin>
			<groupId>org.apache.maven.plugins</groupId>
			<artifactId>maven-compiler-plugin</artifactId>
			<configuration>
				<source>1.8</source>
				<target>1.8</target>
			</configuration>
		</plugin>
		<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>${org.mapstruct.version}</version>
					</path>
				</annotationProcessorPaths>
			</configuration>
		</plugin>
	</plugins>
</build>

2. Creando el modelo, a continuación se muestran las clases de dominio entre las que se encuentran User.java, Role.java, UserDto.java y RoleDto.java. El objetivo del ejemplo será transformar un objeto tipo User.java a uno UserDto.java utilizando MapStruct.

Beans (Pueden ser entities o cualquier tipo de objeto que se desee transformar)

package com.raidentrance.model.pojo;

/**
 * @author raidentrance
 *
 */
public class User {
	private String username;
	private Role role;
	private String password;

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public Role getRole() {
		return role;
	}

	public void setRole(Role role) {
		this.role = role;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
}
package com.raidentrance.model.pojo;

/**
 * @author raidentrance
 *
 */
public class Role {

	private String name;

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

Dto’s (Los objetos a los que serán transformados los beans )

package com.raidentrance.model.dto;

import com.raidentrance.model.pojo.Role;

/**
 * @author raidentrance
 *
 */
public class UserDto  {
	private String username;
	private Role role;
	private String password;

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public Role getRole() {
		return role;
	}

	public void setRole(Role role) {
		this.role = role;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

}
package com.raidentrance.model.dto;

/**
 * @author raidentrance
 *
 */
public class RoleDto  {
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

}

3. Una vez que se cuenta con las clases desde y a las que se va a transformar se debe crear un mapper de MapStruct, el cuál definirá los métodos que se utilizarán para transformar los objetos.

package com.raidentrance.mapper;

import org.mapstruct.Mapper;

import com.raidentrance.model.dto.RoleDto;
import com.raidentrance.model.dto.UserDto;
import com.raidentrance.model.pojo.Role;
import com.raidentrance.model.pojo.User;

/**
 * @author raidentrance
 *
 */
@Mapper
public interface UserMapper {

	UserDto userToUserDto(User user);

	RoleDto roleToRoleDto(Role user);

}

Como se puede observar solo se necesita crear la interfaz más no la implementación, esta será generada por MapStruct cuando se ejecute el goal de maven generate-sources.

4. Probando todo junto.

package com.raidentrance;

import org.mapstruct.factory.Mappers;

import com.raidentrance.mapper.UserMapper;
import com.raidentrance.model.dto.UserDto;
import com.raidentrance.model.pojo.Role;
import com.raidentrance.model.pojo.User;

/**
 * @author raidentrance
 *
 */
public class MapStructSample {
	public static User buildUser() {
		Role r = new Role();
		r.setName("Admin");
		User user = new User();
		user.setUsername("raidentrance");
		user.setPassword("Super secret");
		user.setRole(r);
		return user;
	}

	public static void main(String[] args) {
		UserMapper instance = Mappers.getMapper(UserMapper.class);
		User user = buildUser();

		UserDto dto = instance.userToUserDto(user);
		System.out.println(dto.getUsername());
		System.out.println(dto.getPassword());
		System.out.println(dto.getRole().getName());
		System.out.println(dto.getRole().getClass());
	}
}

Si no se ha ejecutado el build generate-sources y se intenta ejecutar, se producirá la siguiente excepción:

Exception in thread "main" java.lang.RuntimeException: java.lang.ClassNotFoundException: com.raidentrance.mapper.UserMapperImpl
	at org.mapstruct.factory.Mappers.getMapper(Mappers.java:82)
	at com.raidentrance.MapStructSample.main(MapStructSample.java:26)
Caused by: java.lang.ClassNotFoundException: com.raidentrance.mapper.UserMapperImpl
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at org.mapstruct.factory.Mappers.getMapper(Mappers.java:77)
	... 1 more

Esto se debe a que la clase UserMapperImpl aún no existe y se debe generar, para hacer esto se debe ejecutar el goal mvn generate-sources. Una vez hecho esto se puede compilar normalmente el proyecto y al ejecutarlo producirá la siguiente salida:

raidentrance
Super secret
Admin
class com.raidentrance.model.pojo.Role

Puedes encontrar el código completo del ejemplo en el siguiente enlace: https://github.com/raidentrance/mapstruct-example

Autor:

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com

Anuncios

2 Comentarios »

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s