No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Curso de Java Spring

Curso de Java Spring

Alejandro Ramírez

Alejandro Ramírez

Orientar nuestra API al dominio con MapStruct

20/35
Recursos

¿Cómo crear un paquete y una interfaz para los mapeadores?

Al trabajar en desarrollos de software, es esencial contar con herramientas que faciliten la transformación de datos entre diferentes capas del sistema. Una de esas herramientas es MapStruct, que nos permitirá generar mapeadores para traducir objetos de dominio a entidades y viceversa. Para comenzar, debemos:

  1. Crear un nuevo paquete en nuestro proyecto, al cual llamaremos Mapper.
  2. Dentro de este paquete, necesitamos crear interfases específicas para cada mapeador: CategoryMapper y ProductMapper.

Esto se hace seleccionando "new package" y posteriormente "new interfaz" en nuestro IDE. Estos mapeadores serán fundamentales para asegurar que las conversiones se realicen de manera eficiente y precisa.

¿Qué anotaciones se deben utilizar en un mapeador?

MapStruct nos ofrece una sintaxis clara y precisa para definir nuestras conversiones:

  • @Mapper: Esta anotación debe añadirse a cada interfaz para indicar que se trata de un mapeador MapStruct.
  • componentModel = "spring": Se establece como parte de @Mapper, permitiendo integrar MapStruct con Spring. Indica que nuestro mapeador debe comportarse como un componente Spring.

Definir estas anotaciones garantiza que MapStruct gestione correctamente la creación de instancias de mapeadores y su integración con el ciclo de vida de Spring.

¿Cómo definir métodos de conversión?

Los métodos de conversión transforman un tipo de objeto en otro. Aquí definimos métodos para convertir Category y Product en sus equivalentes del dominio.

Conversión de Category a categoría

@Mapper(componentModel = "spring")
public interface CategoryMapper {
    @Mapping(source = "idCategoria", target = "categoryId")
    @Mapping(source = "descripcion", target = "category")
    @Mapping(source = "estado", target = "active")
    Category toCategory(Categoria categoria);
}

Aquí, @Mapping configura cómo se mapean fielmente los datos desde idCategoria, descripcion, y estado hacia sus respectivos atributos en la entidad Category.

Conversión inversa y manejo de atributos ignorados

Para realizar mapeos inversos (de Category a Categoria) y asegurarse de ignorar ciertos atributos que no son necesarios en el lado de la entidad, MapStruct permite el uso de:

  • @InheritInverseConfiguration: Facilita que la conversión inversa utilice las mismas reglas ya definidas.
  • Para ignorar campos específicos, utilizamos:
    @Mapping(target = "productos", ignore = true) 
    

Implementar estas configuraciones limita el código redundante y simplifica el manejo de atributos compatibles entre sistemas.

¿Cómo usar mapeadores para productos?

La interfaz ProductMapper sigue un diseño similar:

Metodología de mapeo

@Mapper(componentModel = "spring", uses = CategoryMapper.class)
public interface ProductMapper {
    @Mappings({
        @Mapping(source = "idProducto", target = "productId"),
        @Mapping(source = "nombre", target = "name"),
        @Mapping(source = "precioVenta", target = "price"),
        @Mapping(source = "cantidadStock", target = "stock"),
        @Mapping(source = "estado", target = "active"),
        @Mapping(source = "categoria", target = "category")
    })
    Product toProduct(Producto producto);
}

Aquí, aseguramos la transformación precisa de cada atributo, utilizando CategoryMapper para manejar las conversiones involucradas con el atributo categoria.

Manejo de listas y conversión inversa

La interfaz también debería incluir métodos para manejar listas de productos y realizar la conversión inversa:

List<Product> toProducts(List<Producto> productos);

@InheritInverseConfiguration
Producto toProducto(Product product);

@Mapping(target = "codigoBarras", ignore = true)

Estos métodos optimizan la gestión de múltiples productos y aseguran que todos los casos de uso de conversión están cubiertos de manera eficiente.

¿Qué beneficios ofrece MapStruct en el desarrollo?

MapStruct simplifica la configuración de mapeadores, lo que reduce el tiempo de desarrollo e incrementa la legibilidad, mantenibilidad y precisión de las conversiones de datos. Incentiva a los desarrolladores a estructurar su código de manera modular y reutilizable.

Reflexionar sobre cómo estos mapeadores integran diferentes capas en tu aplicación te abrirá a implementar soluciones más efectivas y robustas en futuros proyectos. Continúa explorando y dominando herramientas como MapStruct para maximizar la eficiencia de tu desarrollo.

Aportes 60

Preguntas 31

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

No hice caso al viajero del tiempo de no tratar de implementar la Aplicación en Inglés desde el principio 😃
A rehacer, mejor más aprendemos!

Esto del mapper, si me revento la cebeza

Excelente! Me impresiona la manera en la que mapstruct simplifica el código.

En java antes de saber que existe mapStruct usaba ModelMapper,
el uso es mas simple en modelMapper si se trata de convertir clases con atributos con el mismo nombre por ejemplo de Categoria a CategoriaDto con los mismos atributos, veo que con mapStruct la configuracion es mucho más simple para indicar por ejemplo en este caso atributo con atributo de diferente nombre.

A alguien mas al compilar les dice que productos no está dentro de categoria?

Bendito viajero del tiempo.

Creación de mappers

  • Creación de paquete llamado “mapper” en el paquete “persistence”.

  • Se crean como interface

  • A cada clase se le añade el sufijo “Mapper”.

  • Se le añade a la clase la anotación @Mapper

  • MapStruct nos ofrece una integración con Spring. Se le especifica a la anotación: @Mapper(componentModel = "spring")

  • Se especifica la conversión: Category toCategory(Categoria categoria);. Se convertirá un entity de tipo Categoria a un dominio de tipo Category.

  • Se añade la siguiente estructura de anotaciones:

    @Mappings({
    	@Mapping(source = "idCategoria", target = "categoryId"),
    	@Mapping(source = "descripcion", target = "category"),
    })
    Category toCategory(Categoria categoria);
    
  • Se debe hacer el mapeo en sentido contrario. Con la anotación @InheritInverseConfiguration

    • Además, se ignoran los atributos que no se deseen incluir: @Mapping(target = "productos", ignore = true)
    @InheritInverseConfiguration
    @Mapping(target = "productos", ignore = true)
    Categoria toCategoria(Category category);
    
  • Cuando se mapea un atributo que es de una clase que tiene su mapper propio, dentro de la anotación @Mapper, se añade algo como lo siguiente: @Mapper(componentModel = "spring", uses = {CategoryMapper.class})

Si les marca error al hacer el mapeo de categorias y productos a mi me lo soluciono el poner los setters y getters

Muy buen aporte! Ejemplos muy útiles para aprovechar mapstruct.

Codigo de la clase

@Mapper(componentModel = "spring")
public interface CategoryMapper {
    @Mappings({
            @Mapping(source = "idCategoria", target = "categoryId"),
            @Mapping(source = "descripcion", target = "category"),
            @Mapping(source = "estado", target = "active"),
    })
    Category toCategory(Categoria categoria);

    @InheritInverseConfiguration
    @Mapping(target = "productos", ignore = true)
    Categoria toCategoria(Category category);
}

para ignorar un atributo se escribe el siguiente codigo , se ignora los campos que se relacciona con otras tablas o campos que no necesitemos, siempre se debe ignorar si no se agrega en los campos Mappings

@Mapping(target = "nombreCampo", ignore = true)

Tengo entendido que a la hora de crear Interfaces es buena practica anteponer una i mayúscula, para denotar que se trata de una interfaz y no una clase.

Algo así
Interface: ICategoryMapper -> fijense en la (i) que viene primero.

Para los que tengan problemas con el @Data de lombok, nn vez de usar @Data se deben usar @Getter y @Setter, así el lombok no genera problemas.
Adicional colocar annotationProcessor:

implementation 'org.projectlombok:lombok:1.18.22'
    annotationProcessor 'org.projectlombok:lombok:1.18.22'

Dudo mucho que en un proyecto real exista el espanglish que hay metido aqui, esto es un desastre, en mi opinion 😦

@Mapper(componentModel = "spring", uses = {CategoryMapper.class})
public interface ProductMapper {
    @Mappings({
            @Mapping(source = "idProducto", target = "productId"),
            @Mapping(source = "nombre", target = "name"),
            @Mapping(source = "idCategoria", target = "categoryId"),
            @Mapping(source = "precioVenta", target = "price"),
            @Mapping(source = "cantidadStock", target = "stock"),
            @Mapping(source = "estado", target = "active"),
            @Mapping(source = "categoria", target = "category"),
    })
    Product toProduct(Producto producto);
    List<Product> toProducts(List<Producto> productos);

    @InheritInverseConfiguration
    @Mapping(target = "codigoBarras", ignore = true)
    Producto toProducto(Product product);
}

Para indicarle a Spring que tiene que pasar el campo categoria a Category debe usar CategoryMapper.class

@Mapper(componentModel = "spring", uses = {CategoryMapper.class})
Tip: Cuando usas una versión de java posterior a la 8 puedes evitar el agrupamiento de anotaciones y simplemente puedes declararlas tal cual una después de otra. por ejemplo: ![](https://static.platzi.com/media/user_upload/image-d5f6908e-dd1d-457d-96f0-6ed6c8a79de3.jpg) Fuente: SonarLint

Si las variables se llaman igual, no es necesario hacer el @Mapping. La utilidad sería entonces por ejemplo no mapear algunas variables (en este caso el código de barras, por ejemplo).

A veces algunas columnas se llaman igual y otras no, por ejemplo “email” a veces lo llaman igual en bases de datos en español, entonces no sería necesario hacer el mapping de esta.

Buenas no me carga el vídeo 22, pase al vídeo 23 y funciona perfecto

Es normal que en este punto si ejecuto la aplicacion obtenga el siguiente error:
error: No property named “idCreacion” exists in source parameter(s). Did you mean “null”?
@Mapping(source = “idCreacion”, target = “id”)

Cabe recalcar que estoy usando otra base de datos y por ende otras entidades, sin embargo me he guiado completamente del curso, cambiando solo el nombre o tipo de variable

La primera vez que lo vi no entendi mucho, pero me tome un break regrese y lo vi a 0.85x y lo entendi todo.

MaspStruct es muy útil cuando se trabaja en proyectos pequeños, pero ya cuando nos enfrentamos a proyectos más grandes, se puede tornar confuso y difícil de mantener. También encontré algunos problemas de compatibilidad con las versiones actuales, ya que han venido cambiando la forma de hacer este mapper, sobre todo en la 1.5.2, ya que no es muy amigable con spring y las entity.

La anotación @Mappings no es necesaria (al menos en versiones recientes). Por ejemplo, en el mapper de productos pueden tener lo siguiente:

@Mapping(source = "idProducto", target = "productId")
@Mapping(source = "nombre", target = "name")
Product toProducto(Producto producto)

dfghdgfhdf

Hola Alejandro, no resulta el mapstruct con vsc, pero si con intellij… sabes o alguien sabe como poder habilitarlo para vsc…?

Gracias 😃

Hola, el curso está excelente, ya que no solo es programar a la mala y hacer que funcione, sino que desde el principio está la arquitectura y buenas prácticas. Sin embargo, a nivel académico lo del inglés está bueno, pero en la vida real, yo quiero que mi equipo programe más rapido, no le voy a decir lo haces en español y luego en el domain lo reescribes a inglés, ¿de qué manera se debe trabajar para que el desarrollo sea más rápido? Yo trabajo con maven y el plugin se tiene que agregar en el pom, cuando mencionó el plugin de soporte para mapstruct pensé "ah ya seguro me va generar un mapeo automático para evitar escribir todo esos source y target, pues si son muchos atributos demora". Se me ocurre quizás al definir el entity, renombar ya los campos como quieren que se llame finalmente, por ejemplo tengo un campo "text\_nombre" y lo defino en la clase Entity como "name" (aunque la verdad nosotros usamos español y evitamos inglés para no mezclar idiomas), de esta forma en el mapper ya no hay que hacer tanto código.

al compilar spring no encuentra el mapper con autowired . alguien tiene ese error ?

Vamos a crear los mappers para transformar los categories en categorias y products en productos, es como hacer un puente entre las definiciones de entitis

MapStruct es bastante fiable, me faltaban algunos getters/setters de las clases y el compilador es bastante específico con lo que hace falta, bastante útil en caso de hacer refactoring para algún property que suele pasar aunque de todas formas haya que hacer los ajustes en los strings que definen las relaciones

Me encanta que Spring Boot no reinveta la rueda y 99% de las cosas que se ocupan en el backend ya están hechas, simplemente es saber como usar bien las anotaciones y la inyección de dependencia

No me quedo claro como es que esto:
@Mapping(source = “categoria”, target = “category”)

Sabe que tiene que usar este mappeador?
uses = {CategoryMapper.class}

No veo donde se haga la relacion entre los nombres category y CategoryMapper.class.

¿Dónde se puede leer teoría acerca de esta arquitectura?

y si la bd esta en ingles … y las clases en ingles…se necesita igual mapear?..

Es importante que todas las variables que están en los entitys tengan getter and setter, si no después no compilaran.

Sigo sin entender la función del mapping, mejor de una vez los nombres en ingles no? Y nos ahorramos todo esto.

Genial! lo que yo solía hacer (hasta ahora) era crear una clase entidad para la persistencia de los datos y creaba dos clases aparte para las peticiones y las respuestas de este objeto para la API.

**error: No property named "categoria" exists in source parameter(s). Did you mean "idCategoria"?** **@Mapping(source = "categoria", target = "category"),** **^**
Tengo una duda, por que el profesor no aplica en el ProductMapper en el mapeo inverso: @Mapping del codigo de barras... @Mapping(target = "comprasProducto", ignore = "true") ? Producto toProducto(Product producto);
Quiero agradecer por esta lección sobre MapStruct. La explicación fue clara, directa y muy agradable. Me ayudó a entender cómo usar esta herramienta para mapear objetos de manera eficiente.
Está genial la anotación `InheritInverseConfiguration` 😮
Que pasa si la empresa para la que trabajo no me permite usar IntelliJ? lo que aprendí con MapStruct se va al caño y no sabre que hacer... Pienso que al ser cursos iniciales para la mayoría no deberían implementar este tipo de herramientas, si no, hacer de la manera nativa... Ya me decepcione del curso....
¿Es posible construir el aplicativo sin el uso de MapStruct? Es decir, sin depender de generadores de código externos como este.
El uso de Mapper me dejo loco, con lo facil que queda. Ya habia usado uno con los DTO pero tocaba que las clases se llamaran igual y no era la idea. Se me ocurre que debe existir o se podria crear una herramienta que agarrando las clases se peuda crear mas rapido y tal vez grafico los mapper, porque veo que es una tarea muy mecanica.
No me permite correr la aplicación por unos errores que me salen ahi, segun los comentarios dicen que si tiene los getter y settter pero si los tienen. ![](https://static.platzi.com/media/user_upload/image-b27753d5-3c1e-44e4-9c33-cce8553d05f2.jpg)Justamnente son los mapper ![](https://static.platzi.com/media/user_upload/image-67e1a038-f068-415a-ba9c-e7c9923fa3df.jpg) Segun sale como que no los reconoce pero ya estan agregados arriba en con el class..... y no puedo abrir el proyecto en el ecplise spring boot porque dice que esta dañado el .project pero esta bien. ![](https://static.platzi.com/media/user_upload/image-39a0fc1c-833f-4c60-9786-337809f8e900.jpg) ya le di mil vueltas desde el año pasado y nada...
En la notación inicial se puede agregar lo siguiente `@Mapper(componentModel = "spring",` ` unmappedSourcePolicy = ReportingPolicy.``IGNORE``,` ` unmappedTargetPolicy = ReportingPolicy.``IGNORE``)` con la politica de IGNORE como una opción generalizada de `@Mapping(target = "productos", ignore = true)`
Buenas despues de hacer los mappers no me deja ejecutar el proyecto, estoy teniendo dos errores relacionados con los mappers, adjunto foto de los errores y los ficheros señalando donde se producen pero no entiendo por que, segun ![]()creo no deber![]()ian de estar produciendose
GG

Chale todo hiba bien hasta esta clase 🥲😭, hay que hacer ajustes para que compile el proyecto

Muy bien explicado todo.

Hermoso como me simplificó todo el código de mappeo de DTO a Entity y viceversa en mis proyectos personales y en el trabajo. Muchísimas gracias.

Quien tenga problemas con @Mapper y el plugin, es porque a partir de la version 1.5.0 de Mapstruct el pluggin omitio esta anotacion, por eso instalen la version 1.3.0

Que genial clase. Este tema en especial, me voló el cerebro. Párrafo aparte para el profesor, que es un genio y explica muy bien.

¿Y quépasa con los java expressions y los imports? ¿y los decoradores?

MapStruct

Mapear los entity a las objetos de dominio (Hacer el traductor entre la capa de la persistencia y la capa de dominio=

Mapear atributos que son clases.

Enorme diferencia en simplicidad con respecto a ModelMapper

Para que sirve agregar este metodo?
List<Product> toProducts(List<Producto> productos);
y porque en categoria no hicimos algo parecido?

A alguien mas no le carga este video?

Si estás usando lombok debes incluir el annotationProcessor para que te funcione el mapper.

dependencies {
...
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
}