Spring Boot 3 - Desarrollo API Rest: Aula 4
This commit is contained in:
parent
fb7ea652e4
commit
c391edc321
@ -1,18 +1,24 @@
|
|||||||
package med.voll.api.controller;
|
package med.voll.api.controller;
|
||||||
|
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
|
import med.voll.api.medico.DatosListadoMedicos;
|
||||||
import med.voll.api.medico.DatosRegistroMedico;
|
import med.voll.api.medico.DatosRegistroMedico;
|
||||||
import med.voll.api.medico.Medico;
|
import med.voll.api.medico.Medico;
|
||||||
import med.voll.api.medico.MedicoRepository;
|
import med.voll.api.medico.MedicoRepository;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.web.PageableDefault;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/medicos")
|
@RequestMapping("/medicos")
|
||||||
public class MedicoController {
|
public class MedicoController {
|
||||||
|
|
||||||
// No es recomendable usar @Autowired a nivel de declaración pues genera
|
/* No es recomendable usar @Autowired a nivel de declaración pues genera
|
||||||
// problemas al realizar pruebas unitarias
|
problemas al realizar pruebas unitarias */
|
||||||
@Autowired
|
@Autowired
|
||||||
private MedicoRepository medicoRepository;
|
private MedicoRepository medicoRepository;
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@ -20,4 +26,8 @@ public class MedicoController {
|
|||||||
medicoRepository.save(new Medico(datosRegistroMedico));
|
medicoRepository.save(new Medico(datosRegistroMedico));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public Page<DatosListadoMedicos> listadoMedicos(@PageableDefault(size = 5) Pageable paginacion) {
|
||||||
|
return medicoRepository.findAll(paginacion).map(DatosListadoMedicos::new);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
package med.voll.api.controller;
|
||||||
|
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import med.voll.api.paciente.DatosListadoPacientes;
|
||||||
|
import med.voll.api.paciente.DatosRegistroPaciente;
|
||||||
|
import med.voll.api.paciente.Paciente;
|
||||||
|
import med.voll.api.paciente.PacienteRepository;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.web.PageableDefault;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/pacientes")
|
||||||
|
public class PacienteController {
|
||||||
|
|
||||||
|
/* No es recomendable usar @Autowired a nivel de declaración pues genera
|
||||||
|
problemas al realizar pruebas unitarias */
|
||||||
|
@Autowired
|
||||||
|
private PacienteRepository pacienteRepository;
|
||||||
|
@PostMapping
|
||||||
|
public void registrarPaciente(@RequestBody @Valid DatosRegistroPaciente datosRegistroPaciente) {
|
||||||
|
pacienteRepository.save(new Paciente(datosRegistroPaciente));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public Page<DatosListadoPacientes> listadoPacientes(@PageableDefault(size = 5) Pageable paginacion) {
|
||||||
|
return pacienteRepository.findAll(paginacion).map(DatosListadoPacientes::new);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package med.voll.api.medico;
|
||||||
|
|
||||||
|
public record DatosListadoMedicos(String nombre, String especialidad, String documento, String email) {
|
||||||
|
|
||||||
|
public DatosListadoMedicos (Medico medico) {
|
||||||
|
this(medico.getNombre(),
|
||||||
|
medico.getEspecialidad().toString(),
|
||||||
|
medico.getDocumento(),
|
||||||
|
medico.getEmail());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -22,7 +22,7 @@ public class Medico {
|
|||||||
private String email;
|
private String email;
|
||||||
private String telefono;
|
private String telefono;
|
||||||
private String documento;
|
private String documento;
|
||||||
@Enumerated
|
@Enumerated(EnumType.STRING)
|
||||||
private Especialidad especialidad;
|
private Especialidad especialidad;
|
||||||
@Embedded
|
@Embedded
|
||||||
private Direccion direccion;
|
private Direccion direccion;
|
||||||
@ -30,8 +30,8 @@ public class Medico {
|
|||||||
public Medico(DatosRegistroMedico datosRegistroMedico) {
|
public Medico(DatosRegistroMedico datosRegistroMedico) {
|
||||||
this.nombre = datosRegistroMedico.nombre();
|
this.nombre = datosRegistroMedico.nombre();
|
||||||
this.email = datosRegistroMedico.email();
|
this.email = datosRegistroMedico.email();
|
||||||
this.telefono = datosRegistroMedico.telefono();
|
|
||||||
this.documento = datosRegistroMedico.documento();
|
this.documento = datosRegistroMedico.documento();
|
||||||
|
this.telefono = datosRegistroMedico.telefono();
|
||||||
this.especialidad = datosRegistroMedico.especialidad();
|
this.especialidad = datosRegistroMedico.especialidad();
|
||||||
this.direccion = new Direccion(datosRegistroMedico.direccion());
|
this.direccion = new Direccion(datosRegistroMedico.direccion());
|
||||||
}
|
}
|
||||||
|
@ -2,4 +2,5 @@ package med.voll.api.medico;
|
|||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
public interface MedicoRepository extends JpaRepository<Medico, Long> {}
|
public interface MedicoRepository extends JpaRepository<Medico, Long> {
|
||||||
|
}
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
package med.voll.api.paciente;
|
||||||
|
|
||||||
|
public record DatosListadoPacientes(String nombre, String documento, String email) {
|
||||||
|
|
||||||
|
public DatosListadoPacientes(Paciente medico) {
|
||||||
|
this(medico.getNombre(),
|
||||||
|
medico.getDocumento(),
|
||||||
|
medico.getEmail());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package med.voll.api.paciente;
|
||||||
|
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import jakarta.validation.constraints.Email;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
|
import med.voll.api.direccion.DatosDireccion;
|
||||||
|
|
||||||
|
public record DatosRegistroPaciente(
|
||||||
|
@NotBlank String nombre,
|
||||||
|
@NotBlank @Email String email,
|
||||||
|
@NotBlank String telefono,
|
||||||
|
@NotBlank @Pattern(regexp = "\\d{4,6}") String documento,
|
||||||
|
@NotNull @Valid DatosDireccion direccion
|
||||||
|
) {}
|
@ -0,0 +1,35 @@
|
|||||||
|
package med.voll.api.paciente;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import med.voll.api.direccion.Direccion;
|
||||||
|
|
||||||
|
@Table(name="pacientes")
|
||||||
|
@Entity(name="Paciente")
|
||||||
|
@Getter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode(of = "id")
|
||||||
|
public class Paciente {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
private String nombre;
|
||||||
|
private String email;
|
||||||
|
private String telefono;
|
||||||
|
private String documento;
|
||||||
|
@Embedded
|
||||||
|
private Direccion direccion;
|
||||||
|
|
||||||
|
public Paciente(DatosRegistroPaciente datosRegistroPaciente) {
|
||||||
|
this.nombre = datosRegistroPaciente.nombre();
|
||||||
|
this.email = datosRegistroPaciente.email();
|
||||||
|
this.documento = datosRegistroPaciente.documento();
|
||||||
|
this.telefono = datosRegistroPaciente.telefono();
|
||||||
|
this.direccion = new Direccion(datosRegistroPaciente.direccion());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package med.voll.api.paciente;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
public interface PacienteRepository extends JpaRepository<Paciente, Long> {
|
||||||
|
}
|
@ -1,3 +1,6 @@
|
|||||||
spring.datasource.url=jdbc:mysql://192.168.0.8/vollmed_api
|
spring.datasource.url=jdbc:mysql://192.168.0.8/vollmed_api
|
||||||
spring.datasource.username=alura
|
spring.datasource.username=alura
|
||||||
spring.datasource.password=alura
|
spring.datasource.password=alura
|
||||||
|
|
||||||
|
spring.jpa.show-sql=true
|
||||||
|
spring.jpa.properties.hibernate.format_sql=true
|
@ -0,0 +1,16 @@
|
|||||||
|
create table pacientes(
|
||||||
|
|
||||||
|
id bigint not null auto_increment,
|
||||||
|
nombre varchar(100) not null,
|
||||||
|
email varchar(100) not null unique,
|
||||||
|
documento varchar(6) not null unique,
|
||||||
|
telefono varchar(20) not null,
|
||||||
|
calle varchar(100) not null,
|
||||||
|
distrito varchar(100) not null,
|
||||||
|
complemento varchar(100),
|
||||||
|
numero varchar(20),
|
||||||
|
ciudad varchar(100) not null,
|
||||||
|
|
||||||
|
primary key(id)
|
||||||
|
|
||||||
|
);
|
20
010_spring_boot/populate_vollmed_db/populate_medicos.sql
Normal file
20
010_spring_boot/populate_vollmed_db/populate_medicos.sql
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
USE vollmed_api;
|
||||||
|
INSERT INTO vollmed_api.medicos(nombre, email, telefono, documento, especialidad, calle, distrito, complemento, ciudad, numero)
|
||||||
|
VALUES
|
||||||
|
('Cuarto Medico', 'dr_4@voll.med', '44444', '444444', 'CARDIOLOGIA', 'calle 4', 'distrito 4', 'prueba', 'Santiago', '4'),
|
||||||
|
('Quinto Medico', 'dr_5@voll.med', '55555', '555555', 'PEDIATRIA', 'calle 5', 'distrito 5', 'prueba', 'Santiago', '5'),
|
||||||
|
('Sexto Medico', 'dr_6@voll.med', '66666', '666666', 'ORTOPEDIA', 'calle 6', 'distrito 6', 'prueba', 'Santiago', '6'),
|
||||||
|
('Septimo Medico', 'dr_7@voll.med', '77777', '777777', 'GINECOLOGIA', 'calle 7', 'distrito 7', 'prueba', 'Santiago', '7'),
|
||||||
|
('Octavo Medico', 'dr_8@voll.med', '88888', '888888', 'CARDIOLOGIA', 'calle 8', 'distrito 8', 'prueba', 'Santiago', '8'),
|
||||||
|
('Noveno Medico', 'dr_9@voll.med', '99999', '999999', 'PEDIATRIA', 'calle 9', 'distrito 9', 'prueba', 'Santiago', '9'),
|
||||||
|
('Decimo Medico', 'dr_10@voll.med', '10101', '101010', 'ORTOPEDIA', 'calle 10', 'distrito 10', 'prueba', 'Santiago', '10'),
|
||||||
|
('Onceavo Medico', 'dr_11@voll.med', '11011', '110111', 'CARDIOLOGIA', 'calle 11', 'distrito 11', 'prueba', 'Santiago', '11'),
|
||||||
|
('Doceavo Medico', 'dr_12@voll.med', '12121', '121212', 'PEDIATRIA', 'calle 12', 'distrito 12', 'prueba', 'Santiago', '12'),
|
||||||
|
('Treceavo Medico', 'dr_13@voll.med', '13131', '131313', 'ORTOPEDIA', 'calle 13', 'distrito 13', 'prueba', 'Santiago', '13'),
|
||||||
|
('Catorceavo Medico', 'dr_14@voll.med', '14141', '141414', 'GINECOLOGIA', 'calle 14', 'distrito 14', 'prueba', 'Santiago', '14'),
|
||||||
|
('Quinceavo Medico', 'dr_15@voll.med', '15151', '151515', 'CARDIOLOGIA', 'calle 15', 'distrito 15', 'prueba', 'Santiago', '15'),
|
||||||
|
('Diesiceisavo Medico', 'dr_16@voll.med', '16161', '161616', 'PEDIATRIA', 'calle 16', 'distrito 16', 'prueba', 'Santiago', '16'),
|
||||||
|
('Diesicieteavo Medico', 'dr_17@voll.med', '17171', '171717', 'ORTOPEDIA', 'calle 17', 'distrito 17', 'prueba', 'Santiago', '17'),
|
||||||
|
('Diesiochoavo Medico', 'dr_18@voll.med', '18181', '181818', 'GINECOLOGIA', 'calle 18', 'distrito 18', 'prueba', 'Santiago', '18'),
|
||||||
|
('Diecinueveavo Medico', 'dr_19@voll.med', '19191', '191919', 'CARDIOLOGIA', 'calle 19', 'distrito 19', 'prueba', 'Santiago', '19'),
|
||||||
|
('Veinteavo Medico', 'dr_20@voll.med', '20202', '202020', 'PEDIATRIA', 'calle 20', 'distrito 20', 'prueba', 'Santiago', '20');
|
20
010_spring_boot/populate_vollmed_db/populate_pacientes.sql
Normal file
20
010_spring_boot/populate_vollmed_db/populate_pacientes.sql
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
USE vollmed_api;
|
||||||
|
INSERT INTO vollmed_api.pacientes(nombre, email, telefono, documento, calle, distrito, complemento, ciudad, numero)
|
||||||
|
VALUES
|
||||||
|
('Cuarto Paciente', 'paciente_4@private.cl', '44444', '444444', 'calle 4', 'distrito 4', 'prueba', 'Santiago', '4'),
|
||||||
|
('Quinto Paciente', 'paciente_5@private.cl', '55555', '555555', 'calle 5', 'distrito 5', 'prueba', 'Santiago', '5'),
|
||||||
|
('Sexto Paciente', 'paciente_6@private.cl', '66666', '666666', 'calle 6', 'distrito 6', 'prueba', 'Santiago', '6'),
|
||||||
|
('Septimo Paciente', 'paciente_7@private.cl', '77777', '777777', 'calle 7', 'distrito 7', 'prueba', 'Santiago', '7'),
|
||||||
|
('Octavo Paciente', 'paciente_8@private.cl', '88888', '888888', 'calle 8', 'distrito 8', 'prueba', 'Santiago', '8'),
|
||||||
|
('Noveno Paciente', 'paciente_9@private.cl', '99999', '999999', 'calle 9', 'distrito 9', 'prueba', 'Santiago', '9'),
|
||||||
|
('Decimo Paciente', 'paciente_10@private.cl', '10101', '101010', 'calle 10', 'distrito 10', 'prueba', 'Santiago', '10'),
|
||||||
|
('Onceavo Paciente', 'paciente_11@private.cl', '11011', '110111', 'calle 11', 'distrito 11', 'prueba', 'Santiago', '11'),
|
||||||
|
('Doceavo Paciente', 'paciente_12@private.cl', '12121', '121212', 'calle 12', 'distrito 12', 'prueba', 'Santiago', '12'),
|
||||||
|
('Treceavo Paciente', 'paciente_13@private.cl', '13131', '131313', 'calle 13', 'distrito 13', 'prueba', 'Santiago', '13'),
|
||||||
|
('Catorceavo Paciente', 'paciente_14@private.cl', '14141', '141414', 'calle 14', 'distrito 14', 'prueba', 'Santiago', '14'),
|
||||||
|
('Quinceavo Paciente', 'paciente_15@private.cl', '15151', '151515', 'calle 15', 'distrito 15', 'prueba', 'Santiago', '15'),
|
||||||
|
('Diesiceisavo Paciente', 'paciente_16@private.cl', '16161', '161616', 'calle 16', 'distrito 16', 'prueba', 'Santiago', '16'),
|
||||||
|
('Diesicieteavo Paciente', 'paciente_17@private.cl', '17171', '171717', 'calle 17', 'distrito 17', 'prueba', 'Santiago', '17'),
|
||||||
|
('Diesiochoavo Paciente', 'paciente_18@private.cl', '18181', '181818', 'calle 18', 'distrito 18', 'prueba', 'Santiago', '18'),
|
||||||
|
('Diecinueveavo Paciente', 'paciente_19@private.cl', '19191', '191919', 'calle 19', 'distrito 19', 'prueba', 'Santiago', '19'),
|
||||||
|
('Veinteavo Paciente', 'paciente_20@private.cl', '20202', '202020', 'calle 20', 'distrito 20', 'prueba', 'Santiago', '20');
|
@ -330,7 +330,8 @@ Documentación Java
|
|||||||
|
|
||||||
## Agregando dependencias
|
## Agregando dependencias
|
||||||
|
|
||||||
Copiar `xml` de Spring [initializr](https://start.spring.io/), y pegar en `pom.xml`
|
Copiar sección del `xml` de Spring [initializr](https://start.spring.io/), y
|
||||||
|
pegar en `pom.xml`
|
||||||
|
|
||||||
Modificar `resources/application.properties`
|
Modificar `resources/application.properties`
|
||||||
|
|
||||||
@ -413,10 +414,11 @@ public class ProductoDao {
|
|||||||
En el ejemplo anterior, se utilizó JPA como tecnología de persistencia de datos
|
En el ejemplo anterior, se utilizó JPA como tecnología de persistencia de datos
|
||||||
de la aplicación.
|
de la aplicación.
|
||||||
|
|
||||||
**Patrón Repository**. Según el famoso libro Domain-Driven Design de Eric Evans:
|
**Patrón Repository** según el famoso libro *Domain-Driven Design* de *Eric
|
||||||
|
Evans*:
|
||||||
|
|
||||||
El repositorio es un mecanismo para encapsular el almacenamiento, recuperación y
|
> *El repositorio es un mecanismo para encapsular el almacenamiento, recuperación y
|
||||||
comportamiento de búsqueda, que emula una colección de objetos.
|
comportamiento de búsqueda, que emula una colección de objetos.*
|
||||||
|
|
||||||
En pocas palabras, un repositorio también maneja datos y oculta consultas
|
En pocas palabras, un repositorio también maneja datos y oculta consultas
|
||||||
similares a DAO. Sin embargo, se encuentra en un nivel más alto, más cerca de
|
similares a DAO. Sin embargo, se encuentra en un nivel más alto, más cerca de
|
||||||
@ -516,3 +518,206 @@ estableciendo una colaboración entre componentes.
|
|||||||
Para más información sobre la anotación, ver la
|
Para más información sobre la anotación, ver la
|
||||||
[documentación oficial](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/annotation/Autowired.html)
|
[documentación oficial](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/annotation/Autowired.html)
|
||||||
|
|
||||||
|
## Get
|
||||||
|
|
||||||
|
### Consideraciones
|
||||||
|
|
||||||
|
Información requerida del médico
|
||||||
|
|
||||||
|
- Nombre
|
||||||
|
- Especialidad
|
||||||
|
- Documento
|
||||||
|
- Email
|
||||||
|
|
||||||
|
Reglas del negocio
|
||||||
|
|
||||||
|
- Orden ascendente
|
||||||
|
- Paginado, máximo 10 registros por página
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
En
|
||||||
|
[MedicoController](./api_rest/api/src/main/java/med/voll/api/controller/MedicoController.java)
|
||||||
|
se utilizo DTO para representar los datos recibi4os y devueltos a través de la
|
||||||
|
API, *¿Por qué, en lugar de crear un DTO, no devolvemos directamente la entidad
|
||||||
|
JPA en el Controller?*. Para esto, basta con cambiar método `listadoMedicos()`
|
||||||
|
en el Controller a:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@GetMapping
|
||||||
|
public List<Medico> listarMedicos() {
|
||||||
|
return repository.findAll();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
De esa forma, el código sería más ligero y no necesitaríamos crear el DTO
|
||||||
|
en el proyecto. Pero, **¿es esto realmente una buena idea?**
|
||||||
|
|
||||||
|
#### Problemas de recepción/devolución de la entidad JPA
|
||||||
|
|
||||||
|
De hecho, es mucho más simple y cómodo no usar DTO, sino tratar directamente
|
||||||
|
con entidades JPA en los Controllers. Sin embargo, este enfoque tiene algunas
|
||||||
|
desventajas, incluida la vulnerabilidad de la aplicación a los ataques de
|
||||||
|
*Mass Assignment*.
|
||||||
|
|
||||||
|
Uno de los problemas, es que al devolver una entidad JPA en un método del
|
||||||
|
Controller, Spring generará el JSON que contiene todos sus atributos, y este no
|
||||||
|
siempre es el comportamiento deseado.
|
||||||
|
|
||||||
|
Eventualmente pueden tener atributos que no se requiere que sean devueltos en el
|
||||||
|
JSON, ya sea por razones de seguridad, en el caso de datos sensibles, o incluso
|
||||||
|
porque no son utilizados por clientes API.
|
||||||
|
|
||||||
|
#### Uso de la anotación `@JsonIgnore`
|
||||||
|
|
||||||
|
En esta situación, se puede usar la anotación `@JsonIgnore`, que nos ayuda a
|
||||||
|
ignorar ciertas propiedades de una clase Java cuando se serializa en un objeto
|
||||||
|
JSON.
|
||||||
|
|
||||||
|
Su uso consiste en agregar la anotación a los atributos que se quieren ignorar
|
||||||
|
cuando se genera el JSON. Por ejemplo, supongamos que tenemos una entidad JPA
|
||||||
|
`Empleado`, en la que se quiere ignorar el atributo `salario`:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Getter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@EqualsAndHashCode(of = "id")
|
||||||
|
@Entity(name = "Empleado")
|
||||||
|
@Table(name = "empleados")
|
||||||
|
public class Empleado {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
private String nombre;
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private BigDecimal salario;
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
En el ejemplo anterior, el atributo `salario` de la clase `Empleado` no será
|
||||||
|
mostrado en las respuestas JSON y el problema estaría resuelto.
|
||||||
|
|
||||||
|
Sin embargo, puede haber algún otro endpoint de la API en el que necesitemos
|
||||||
|
enviar el salario de los empleados en el JSON, en cuyo caso se tendrían problemas,
|
||||||
|
ya que con la anotación `@JsonIgnore` tal atributo nunca se enviará en el JSON,
|
||||||
|
y al eliminar la anotación se enviará el atributo siempre. Por lo tanto,
|
||||||
|
se pierde la flexibilidad de controlar cuándo se deben enviar ciertos atributos
|
||||||
|
en el JSON y cuándo no.
|
||||||
|
|
||||||
|
#### DTO
|
||||||
|
|
||||||
|
El patrón **DTO** (***Data Transfer Object***) es un patrón arquitectónico que
|
||||||
|
se usó ampliamente en aplicaciones Java distribuidas (arquitectura
|
||||||
|
cliente/servidor) para representar los datos que eran enviados y recibidos entre
|
||||||
|
aplicaciones cliente y servidor.
|
||||||
|
|
||||||
|
El patrón **DTO** puede (y debe) usarse cuando no queremos exponer todos los
|
||||||
|
atributos de alguna entidad en nuestro proyecto, una situación similar a los
|
||||||
|
salarios de los empleados que discutimos anteriormente. Además, con la
|
||||||
|
flexibilidad y la opción de filtrar qué datos se transmiten, podemos ahorrar
|
||||||
|
tiempo de procesamiento.
|
||||||
|
|
||||||
|
#### Bucle infinito que causa StackOverflowError
|
||||||
|
|
||||||
|
Otro problema muy recurrente cuando se trabaja directamente con entidades JPA
|
||||||
|
ocurre cuando una entidad tiene alguna *auto-relación* o *relación
|
||||||
|
bidireccional*. Por ejemplo, considere las siguientes entidades JPA:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Getter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@EqualsAndHashCode(of = "id")
|
||||||
|
@Entity(name = "Producto")
|
||||||
|
@Table(name = "productos")
|
||||||
|
public class Producto {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
private String nombre;
|
||||||
|
private String descripcion;
|
||||||
|
private BigDecimal precio;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = “id_categoria”)
|
||||||
|
private Categoria categoria;
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Getter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@EqualsAndHashCode(of = "id")
|
||||||
|
@Entity(name = "Categoria")
|
||||||
|
@Table(name = "categorias")
|
||||||
|
public class Categoria {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
private String nombre;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = “categoria”)
|
||||||
|
private List<Producto> productos = new ArrayList<>();
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Al devolver un objeto de tipo `Producto` en el Controller, Spring tendría
|
||||||
|
problemas para generar el JSON de este objeto, lo que provocaría una excepción
|
||||||
|
de tipo `StackOverflowError`. Este problema ocurre porque el objeto producto
|
||||||
|
tiene un atributo de tipo `Categoria`, que a su vez tiene un atributo de tipo
|
||||||
|
`Lista<Producto>` lo que provoca un bucle infinito en el proceso de serialización
|
||||||
|
a JSON.
|
||||||
|
|
||||||
|
Este problema se puede resolver usando la anotación `@JsonIgnore` o usando las
|
||||||
|
anotaciones `@JsonBackReference` y `@JsonManagedReference`, pero también se puede
|
||||||
|
evitar usando un DTO que represente solo los datos que se deben devolver en el
|
||||||
|
JSON.
|
||||||
|
|
||||||
|
### Paginación y Orden
|
||||||
|
|
||||||
|
Para la paginación se utiliza la interfase `Pageable`
|
||||||
|
|
||||||
|
```java
|
||||||
|
...
|
||||||
|
@GetMapping
|
||||||
|
public Page<DatosListadoMedicos> listadoMedicos(
|
||||||
|
@PageableDefault(size = 5) Pageable paginacion
|
||||||
|
) {
|
||||||
|
return medicoRepository.findAll(paginacion).map(DatosListadoMedicos::new);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Estos pueden pre-establecerse con la anotación `@PageableDefault`, o en el archivo
|
||||||
|
[application.properties](./api_rest/api/src/main/resources/application.properties)
|
||||||
|
|
||||||
|
```ini
|
||||||
|
spring.data.web.pageable.page-parameter=0
|
||||||
|
spring.data.web.pageable.size-parameter=5
|
||||||
|
spring.data.web.sort.sort-parameter=nombre
|
||||||
|
```
|
||||||
|
|
||||||
|
Estos además pueden ser sobrescritos al hacer el request:
|
||||||
|
|
||||||
|
```http
|
||||||
|
http://127.0.0.1:8080/medicos?size=3&page=2&sort=documento,desc
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Ver y formatear querys en el IDE
|
||||||
|
|
||||||
|
En archivo [application.properties](./api_rest/api/src/main/resources/application.properties)
|
||||||
|
|
||||||
|
```ini
|
||||||
|
spring.jpa.show-sql=true
|
||||||
|
spring.jpa.properties.hibernate.format_sql=true
|
||||||
|
```
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user