Las clases DTO son clases usadas como contenedores de datos sin ninguna lógica o con muy poca, se construyen con datos copiados de otras clases. Un uso de estas clases DTO es para evitar emplear el uso del patrón Open Session in View ya que aunque ofrece algunos beneficios también tiene algunos inconvenientes. La librería ModelMapper permite realizar los copiados de datos de un objeto origen a una nueva instancia destino de otra clase.
En ocasiones es necesario copiar datos de un tipo de objeto a otro tipo, no es una operación complicada basta con llamar al método getter de la propiedad a copiar para obtener su valor del objeto origen y posteriormente llamar al setter para establecer el valor en objeto destino. Aún siendo una operación sencilla es tediosa y puede complicarse si se han de copiar listas de objetos y si esos objetos a copiar tienen referencias a otros objetos que también hay que copiar. Si además esta es una operación común en el código es conveniente utilizar una librería específica para este propósito, una de ellas es ModelMapper.
ModelMapper es una librería Java para copiar o mapear propiedades de un tipo de objeto a otro tipo de objeto, permitiendo copiar también los datos de las referencias a los objetos que contengan. Soporta diferentes convenciones, copiados explícitos, conversiones y proveedores para construir los objetos destino e integraciones con diferentes librerías, una de ellas jOOQ.
Un posible caso de uso es para evitar emplear el patrón Open Session in View ya que tiene varios inconvenientes. Con una librería como ModelMapper es posible hacer uso de simples objetos contenedores de datos en la vista copiando los datos de las entidades a los objetos DTO. O si para obtener los datos de la vista en vez de usar una librería como Hibernate se opta por una librería como jOOQ permitir copiar los datos de los registros de jOOQ a los mismos DTOs.
El siguiente ejemplo se compone de tres clases que tienen relaciones entre ellas, estas clases podrían ser las entidades si se persistiesen en base de datos con Hibernate.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package io.github.picodotdev.blogbitix.modelmapper.classes;
public class Order {
private Customer customer;
private Address billingAddress;
public Order(Customer customer, Address billingAddress) {
this.customer = customer;
this.billingAddress = billingAddress;
}
public Customer getCustomer() {
return customer;
}
public Address getBillingAddress() {
return billingAddress;
}
}
|
Order.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package io.github.picodotdev.blogbitix.modelmapper.classes;
public class Customer {
private String firstName;
private String lastName;
public Customer(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
}
|
Customer.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package io.github.picodotdev.blogbitix.modelmapper.classes;
public class Address {
private String street;
private String city;
public Address(String street, String city) {
this.street = street;
this.city = city;
}
public String getStreet() {
return street;
}
public String getCity() {
return city;
}
}
|
Address.java
La clase DTO es simplemente una nueva clase POJO que contiene los datos de las clases anteriores, para evitar el patrón Open Session in View la vista recibiría una instancia de esta clase.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
package io.github.picodotdev.blogbitix.modelmapper.classes;
public class OrderDTO {
private String customerFirstName;
private String customerLastName;
private String billingAddressStreet;
private String billingAddressCity;
public String getCustomerFirstName() {
return customerFirstName;
}
public void setCustomerFirstName(String customerFirstName) {
this.customerFirstName = customerFirstName;
}
public String getCustomerLastName() {
return customerLastName;
}
public void setCustomerLastName(String customerLastName) {
this.customerLastName = customerLastName;
}
public String getBillingAddressStreet() {
return billingAddressStreet;
}
public void setBillingAddressStreet(String billingAddressStreet) {
this.billingAddressStreet = billingAddressStreet;
}
public String getBillingAddressCity() {
return billingAddressCity;
}
public void setBillingAddressCity(String billingAddressCity) {
this.billingAddressCity = billingAddressCity;
}
}
|
OrderDTO.java
En esta aplicación de Spring Boot se construye una instancia de la clase ModelMapper y posteriormente con su configuración y convenciones por defecto realiza el copiado de datos de una instancia de la clase Order a una nueva instancia de la clase OrderDTO. En la salida del programa en la consola se muestran los valores de las propiedades de OrderDTO copiadas de la clase Order.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
package io.github.picodotdev.blogbitix.modelmapper;
import io.github.picodotdev.blogbitix.modelmapper.classes.Address;
import io.github.picodotdev.blogbitix.modelmapper.classes.Customer;
import io.github.picodotdev.blogbitix.modelmapper.classes.Order;
import io.github.picodotdev.blogbitix.modelmapper.classes.OrderDTO;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class Main implements CommandLineRunner {
@Autowired
private ModelMapper modelMapper;
@Bean
ModelMapper modelMapper() {
return new ModelMapper();
}
@Override
public void run(String... args) throws Exception {
Customer customer = new Customer("Francisco", "Ibáñez");
Address billigAddress = new Address("c\\ Rue del Percebe, 13", "Madrid");
Order order = new Order(customer, billigAddress);
OrderDTO orderDTO = modelMapper.map(order, OrderDTO.class);
System.out.printf("Customer First Name: %s%n", orderDTO.getCustomerFirstName());
System.out.printf("Customer Last Name: %s%n", orderDTO.getCustomerLastName());
System.out.printf("Billing Address Street: %s%n", orderDTO.getBillingAddressStreet());
System.out.printf("Billing Address City: %s%n", orderDTO.getBillingAddressCity());
}
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
|
Main.java
1
2
3
4
|
Customer First Name: Francisco
Customer Last Name: Ibáñez
Billing Address Street: c\ Rue del Percebe, 13
Billing Address City: Madrid
|
System.out
El archivo de construcción Gradle contiene la dependencia de ModelMapper.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
plugins {
id 'java'
id 'application'
}
group = 'io.github.picodotdev.blogbitix.modelmapper'
version = '1.0'
java {
sourceCompatibility = JavaVersion.VERSION_11
}
application {
mainClass = 'io.github.picodotdev.blogbitix.modelmapper.Main'
}
repositories {
mavenCentral()
}
dependencies {
implementation platform('org.springframework.boot:spring-boot-dependencies:2.3.0.RELEASE')
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.modelmapper:modelmapper:2.3.7'
}
|
build.gradle
El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando siguiente comando:
./gradlew run