Artículo

MapStruct en Java: Qué Es y Cómo Usarlo con Anotaciones (Ejemplo Completo)

Aprende qué es MapStruct, cómo configurarlo con anotaciones en Spring Boot y mapea Entidades a DTOs con ejemplos de código completos.

MapStruct es una librería Java que genera automáticamente, en tiempo de compilación, el código necesario para mapear un objeto a otro (por ejemplo, de una entidad a un DTO), evitando escribir ese mapeo a mano y sin el coste de rendimiento de las librerías basadas en reflection.

¿Qué es MapStruct?

MapStruct funciona como un annotation processor: analizas una interfaz con las anotaciones adecuadas y, durante la compilación, MapStruct genera una clase de implementación real con el código de mapeo, campo a campo. El resultado es código Java normal, tan rápido como si lo hubieras escrito tú mismo.

¿Por qué usar MapStruct en lugar de mapeo manual?

  • Elimina código repetitivo (boilerplate) de conversión entre capas.
  • Detecta errores de mapeo en tiempo de compilación, no en producción.
  • Mejor rendimiento que librerías basadas en reflection como ModelMapper.
  • El código generado es legible y depurable como cualquier otra clase Java.

Instalación y configuración en un proyecto Spring Boot

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.6.0</version>
</dependency>
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.6.0</version>
    <scope>provided</scope>
</dependency>

Anotaciones clave de MapStruct

@Mapper

Marca una interfaz como un mapper. Con componentModel = "spring", MapStruct genera la implementación como un bean de Spring que puedes inyectar directamente.

@Mapper(componentModel = "spring")
public interface UsuarioMapper {
    UsuarioDTO toDto(Usuario usuario);
}

@Mapping

Permite indicar cómo mapear campos que no coinciden de nombre entre origen y destino.

@Mapping(source = "nombreCompleto", target = "nombre")
UsuarioDTO toDto(Usuario usuario);

@MappingTarget

Permite actualizar un objeto ya existente en lugar de crear uno nuevo, útil en operaciones de tipo update.

void actualizarDesdeDto(UsuarioDTO dto, @MappingTarget Usuario usuario);

Ejemplo práctico: mapear una Entidad a un DTO

Definir el Mapper

@Entity
public class Usuario {
    private Long id;
    private String nombreCompleto;
    private String email;
    // getters y setters
}

public class UsuarioDTO {
    private String nombre;
    private String email;
    // getters y setters
}

@Mapper(componentModel = "spring")
public interface UsuarioMapper {
    @Mapping(source = "nombreCompleto", target = "nombre")
    UsuarioDTO toDto(Usuario usuario);
}

Código generado por MapStruct (explicado)

MapStruct genera automáticamente una clase UsuarioMapperImpl con algo equivalente a:

@Component
public class UsuarioMapperImpl implements UsuarioMapper {
    @Override
    public UsuarioDTO toDto(Usuario usuario) {
        if (usuario == null) return null;
        UsuarioDTO dto = new UsuarioDTO();
        dto.setNombre(usuario.getNombreCompleto());
        dto.setEmail(usuario.getEmail());
        return dto;
    }
}

Uso del Mapper en un servicio Spring

@Service
public class UsuarioService {

    private final UsuarioMapper usuarioMapper;

    public UsuarioService(UsuarioMapper usuarioMapper) {
        this.usuarioMapper = usuarioMapper;
    }

    public UsuarioDTO obtenerUsuario(Usuario usuario) {
        return usuarioMapper.toDto(usuario);
    }
}

Mapeo de colecciones y objetos anidados

MapStruct detecta automáticamente cuándo el tipo de origen o destino es una List o un Set y genera el mapeo elemento a elemento sin configuración adicional, siempre que exista un método de mapeo para el tipo contenido. Para objetos anidados, basta con declarar un mapper independiente para el objeto interno y MapStruct lo reutilizará automáticamente.

MapStruct vs ModelMapper vs mapeo manual

Mapeo manualModelMapperMapStruct
RendimientoÓptimoBasado en reflection (más lento)Óptimo (código generado)
Detección de erroresEn ejecuciónEn ejecuciónEn compilación
MantenimientoAlto (mucho boilerplate)BajoBajo
Curva de aprendizajeNingunaBajaMedia

Errores comunes al usar MapStruct

  • Olvidar añadir mapstruct-processor como dependencia provided, lo que impide que se genere la implementación.
  • No usar componentModel = "spring" y luego intentar inyectar el mapper como si fuera un bean.
  • Nombrar campos de forma distinta entre origen y destino sin declarar el @Mapping correspondiente, generando un mapeo incompleto silencioso.

Preguntas frecuentes

¿MapStruct usa reflection en tiempo de ejecución? No. Todo el código se genera en tiempo de compilación, por lo que en ejecución es tan rápido como código Java escrito a mano.

¿Puedo mapear campos con nombres distintos? Sí, usando @Mapping(source = "...", target = "...").

¿MapStruct funciona sin Spring? Sí, es independiente de Spring; componentModel = "spring" es opcional y solo facilita la inyección como bean.

Siguiente paso

Para aprovechar MapStruct al máximo dentro de un proyecto real, conviene combinarlo con una arquitectura basada en entidades JPA y DTOs bien definidos.

mapstruct java spring boot dto

Newsletter Semanal de Java

Cada viernes recibe lo más nuevo del ecosistema Java: frameworks, herramientas y mejores prácticas.

🔒 Sin spam. Cancela cuando quieras.

Compartir artículo