Chuleta Java
Chuleta Java
Chuleta Java
Imagínate que tienes una aplicación que necesita acceder a información de libros
almacenada en una base de datos. En lugar de acceder directamente a la base de
datos, la aplicación puede hacer solicitudes HTTP (como GET para obtener datos,
POST para crear datos, PUT para actualizar datos y DELETE para eliminar datos) a
una API REST, que luego interactúa con la base de datos y devuelve la información
requerida a la aplicación en un formato que pueda entender y utilizar.
}
public class ReservationDTO {
@Email
private String correoElectronico;
LOMBOK (Nos facilita por ejemplo en hace el constructor, o los getters y setters)
//Una vez descargado lombok.jar, lo ejecutamos e instalamos para el eclipse
que estemos utilizando. Si no da errores ya estará implementada.
//En eclipse en el model hay que poner:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserDTO {
//Con esto solo nos hace falta definir los atributos.
private Integer id;
private String nombre;
BBDD
Para poder trabajar con persistencia en desarrollo con SpringBoot se deben
considerar los siguientes puntos:
b. Levantar un contenedor Docker (run) de una imagen de la bdd que se quiere usar
(en este caso Postgres
a. En ambos casos, el puerto debe ser siempre el puerto local en el que escucha mi
ordenador.
b. Hay que tener cuidado de que no exista colisión de puertos en el caso que tenga
varias instancias de BDDS, por ejemplo, el postgres local y el docker. Ejecutar
comando netstat -an
Si el puerto 5432 está ocupado y necesitas seleccionar otro puerto para alguna
instancia que corra PostgreSQL, por ejemplo, docker, puedes elegir un puerto que
esté dentro del rango de puertos dinámicos y/o privados, que va desde el 49152
hasta el 65535, según la IANA (Internet Assigned Numbers Authority).
Sin embargo, muchos desarrolladores eligen puertos dentro del rango 5433-5440 para
evitar conflictos con otros servicios y mantener una coherencia que facilite la
identificación del servicio que está utilizando el puerto.
i. 5434
ii. 5435
iii. 5436
iv. 5437
v. 5438
vi. 5439
vii. 5440
viii.
d. Si has decidido cambiar el puerto de docker, debes levantar un contenedor
(ejecutar el docker run) asegurándote de hacer un mapeo del puerto local disponible
al puerto 5432 de docker, que es el que usa postgre por defecto.
En donde -p 5437:5432 hace el mapeo del puerto local 5437 (en mi ordenador) al
puerto 5432 por defecto que usa la instancia de postgres que estamos ejecutando en
el contenedor docker.
spring.sql.init.mode=always
spring.jpa.hibernate.ddl-auto=update
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
@Id
Existen más anotaciones para los atributos de las clases (consultar) como por
ejemplo:
//Cuando un campo, muchos de ello pertenezca a un solo, por ejemplo muchos libros
pertenece a un autor, pondremos:
@ManyToOne
@JoinColumn(name = "autor_id")
6. Crear los repositorios de cada clase: por cada clase se debe crear una interface
que extienda de JPARepository y además se debe anotar dicha interface con
@Repository
Ejemplo:
@Repository
public interface BookRepository extends JpaRepository<BookEntity, Integer>{
7. Hacer el mapeo de las clases que usemos como dtos a entities y viceversa.
Para ello:
-Añadir dependencias y los plugins (en caso de usar lombok) en el pomk:
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.5.Final</version>
</dependency>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.5.Final</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
Hecho esto actualizamos el pom :click derecho en nuestro proyecto --> Maven -->
Update proyect
Hecho esto nos creamos un paquete .mappers (esto sirve para enlazar los dtos y los
entities, para poder usar métodos que nos sirva para actualizar la bbdd) y dentro
nuestras interfaces:BookMapper ....
en la interfaz bookMapper:
@Mapper(componentModel = "spring")
public interface BookMapper {
8.Ya nos faltaría cambiar los métodos en los services, usando mapeo.
SERVICES
En Spring Boot, un servicio es una abstracción que define un conjunto de
operaciones relacionadas con la lógica de negocio. El término "servicio" puede
referirse tanto al concepto como a la implementación concreta en código.
Aquí hay algunas características clave de los servicios en Spring Boot y cómo se
utilizan:
Separación de Responsabilidades:
Anotación @Service:
· En Spring, las clases que implementan la lógica de negocio se anotan a menudo con
@Service. Esto le dice a Spring que esta clase es un candidato para la detección
automática al escanear los paquetes de la aplicación y para la inyección de
dependencias.
Transacciones:
· Los servicios son a menudo el lugar donde se manejan las transacciones de base de
datos. Spring proporciona soporte declarativo para transacciones que se pueden
agregar mediante anotaciones como @Transactional.
Inyección de Dependencias:
Singularidad:
· A menudo, las instancias de servicios son beans singleton, lo que significa que
hay una única instancia de un servicio en toda la aplicación. Esto es gestionado
por el contenedor de Spring y ayuda a mantener la coherencia y el estado cuando es
necesario.
Reutilización:
· Los servicios pueden ser reutilizados por diferentes partes de la aplicación. Por
ejemplo, un servicio de autenticación podría ser utilizado por diferentes
controladores que manejan el login o la verificación de la identidad del usuario.
Pruebas:
· Los servicios suelen ser diseñados para ser fácilmente testeables. Esto puede
implicar la definición de interfaces claras y el uso de inyección de dependencias
para intercambiar implementaciones reales con dobles de prueba o mocks.
List<BookDTO> getAllbooks();
}
@Service //Anotación @Service que siempre se tiene que poner al tener una clase
service.
public class BookServiceImpl implements BookService {
public BookServiceImpl() {
libros.add(b1);
libros.add(b2);
libros.add(b3);
}
@Override
public ArrayList<BookDTO> getAllbooks() {
return libros;
}
@Override
public void createBook(BookDTO book) {
libros.add(book);
}
@Override
public Boolean IsReserved(Integer id) {
String code = getCode(id);
for (BookDTO b : libros) {
if (b.getCode().equals(code)) {
return b.getReservado();
}
}
@Override
public String getCode(Integer bookId) {
for (BookDTO b : libros) {
if (b.getId() == bookId) {
return b.getCode();
}
}
@Override
public BookDTO getBook(Integer bookId) {
return null;
}
ArrayList<ReservationDTO> getAllReservations();
@Service
public class ReservationServiceImpl implements ReservationService {
BookService bookService;
UserService userService;
Integer cont = 1;
@Override
public void reserveBook(Integer bookId, Integer userId) {
if (book != null) {
if (book.getReservado()) {
throw new ReservationConflictException("El libro con id: "
+ bookId + " ya se encuentra reservado");
} else {
book.setReservado(true);
ReservationDTO reserva = new ReservationDTO(cont, bookId,
userId, LocalDate.now(), null);
reservas.add(reserva);
cont++;
}
} else {
throw new BookNotFoundException("No extieste el libro con id: " +
bookId);
}
}
@Override
public void cancelReservation(Integer bookId, Integer userId) {
for (ReservationDTO re : reservas) {
} else {
throw new ReservationConflictException(
"No existe una reserva para el user id:" +
userId + " y el libro con id: " + bookId);
}
}
@Override
public ArrayList<ReservationDTO> getActiveReservation(Integer userId) {
ArrayList<ReservationDTO> activeReservations = new ArrayList<>();
} else {
throw new ReservationConflictException(
"La persona con el user id:" + userId + " nunca
ha reservado un libro ");
}
}
return activeReservations;
}
@Override
public ArrayList<ReservationDTO> getAllReservations() {
return reservas;
}
@Override
public ArrayList<ReservationDTO> getAllReservations(Integer userId) {
ArrayList<ReservationDTO> reservations = new ArrayList<>();
if (reservations.isEmpty()) {
throw new ReservationConflictException(
"La persona con el user id:" + userId + " nunca ha
reservado un libro ");
}
return reservations;
}
@Service
public class UserServiceImpl implements UserService{
@Override
public UserDTO getUser(Integer id) {
return null;
}
@Override
public Boolean existUser(Integer id) {
for(UserDTO u : usuarios) {
if(u.getId() ==id) {
return true;
}
}
return false;
}
EXCEPCIONES PERSONALIZADAS
Almacena clases de excepciones personalizadas para manejar errores y problemas
específicos del negocio.
(Sirve en el mercado cuando te piden que se tenga que devolver una excepción
personalizada y no el rollazo del postman)
Para manejar excepciones siguiendo la especificación RFC 7807, podemos utilizar la
anotación @RestControllerAdvice para crear una clase de manejo de excepciones
personalizada. Esta clase debe extender la clase RestControllerAdvice y utilizar el
método @ExceptionHandler() para registrar los manejadores de excepciones.
package edu.cesur.fullstack.exceptions;
}
}
package edu.cesur.fullstack.exceptions;
}
}
import java.net.URI;
import java.time.Instant;
import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import edu.cesur.fullstack.exceptions.ReservationConflictException;
import edu.cesur.fullstack.exceptions.BookNotFoundException;
@RestControllerAdvice
public class GlobalExceptionHandler {
ProblemDetail problemDetail =
ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, e.getMessage()); //Aquí se
pondrá el tipo de excepcion, bad_request, not_found......
problemDetail.setTitle("Book Exception Occurred"); //Esto es para
informar "Book" es lo que se cambia
problemDetail.setType(URI.create("http://cesurformacion.com"));
problemDetail.setProperty("errorCategory", "Book"); //"Book" cambiable
problemDetail.setProperty("timeStamp", Instant.now());
return problemDetail;
}
@ExceptionHandler( ReservationConflictException.class )
public ProblemDetail
handleReservationConflictException(ReservationConflictException e) {
ProblemDetail problemDetail =
ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, e.getMessage());
problemDetail.setTitle("Reservation Exception Occurred");
problemDetail.setType(URI.create("http://cesurformacion.com"));
problemDetail.setProperty("errorCategory", "Reservation");
problemDetail.setProperty("timeStamp", Instant.now());
return problemDetail;
}
@ExceptionHandler( MethodArgumentNotValidException.class )
public ProblemDetail
handleReservationConflictException(MethodArgumentNotValidException e) {
ProblemDetail problemDetail =
ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, e.getLocalizedMessage());
problemDetail.setTitle("Custom Exception Occurred");
problemDetail.setType(URI.create("http://cesurformacion.com"));
problemDetail.setProperty("errorCategory", "Book");
problemDetail.setProperty("timeStamp", Instant.now());
return problemDetail;
}
VALIDACIONES
Almacena iterfaces y clases con validaciones personalizadas.
Antes de hacer una validación en un nuevo proyecto hay que añadir la siguiente
dependencia en el pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
VALIDACIONES SIMPLES
Se colocan encima de los atributos dentro de los models y también hay
que validarla en los atributos de los métodos que use dicho atributo dentro de los
controllers con @Validated.A continuación se mostrará los diferentes
tipos de anotaciones:
@NotNull: Esta anotación se utiliza para marcar un campo o parámetro
como no nulo.
@NotEmpty: Significa que la cadena no debe ser nula y debe tener una
longitud mayor que cero.
@NotBlank: Similar a @NotEmpty, pero se aplica específicamente a
cadenas.
@Size: Puede especificar valores mínimos y máximos para el tamaño.
@Positive: Se utiliza para asegurarse de que un valor numérico sea
estrictamente mayor que cero.
@Negative: Se utiliza para asegurarse de que un valor numérico sea
estrictamente menor que cero.
@Min @Max: Estas anotaciones se utilizan para definir valores mínimos y
máximos para campos numéricos.
@Pattern: Permite especificar una expresión regular que debe coincidir
con el valor del campo.
@Email: Se utiliza para validar que una cadena es una dirección de
correo electrónico válida.
@AssertTrue y @AssertFalse: Estas anotaciones se utilizan para
verificar si una expresión booleana es verdadera o falsa, respectivamente.
@Future y @Past: Estas anotaciones se utilizan para validar fechas.
@Future garantiza que una fecha esté en el futuro, mientras que @Past garantiza que
esté en el pasado.
@Valid: Esta anotación se utiliza para indicar que se debe realizar una
validación recursiva en un objeto anidado. Es útil cuando se trabaja con objetos
complejos que contienen otros objetos.
Ejemplo:
@NotNull
String nombre;
También hay que validarla en los atributos de los métodos que use
String nombre dentro de los controllers:
public ResponseEntity<?> createPersona(@RequestBody @Validated Persona
persona) //@Validated
VALIDACIONES PERSONALIZADAS
Tenemos que crearnos un paquete validators y dentro primero creamos una
@annotation --> Marcamos Add @Retention --> Runtime --> Add @Target --> Field
package edu.cesur.fullstack.validators;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
@Retention(RUNTIME)
@Target(FIELD)
@Constraint(validatedBy = BookCodeValid.class)
public @interface BookCodeValidator {
String message() default "Código inválido."
+ " Debe contener exactamente 3 letras seguidas de 3 números.";
//Clase validadora
package edu.cesur.fullstack.validators;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class BookCodeValid implements ConstraintValidator<BookCodeValidator,
String> {//BookCodeValidator será el nombre de la interfaz de anotacion ,String es
donde ponemos el tipo de archivo que queremos validar
@Override
public void initialize(BookCodeValidator constraintAnnotation) {
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
log.info("Código null");
return false;
}
return Pattern.compile("^[A-Za-z]{3}\\d{3}$").matcher(value).matches();
//directo
}
en el controller:
public ResponseEntity<?> createPersona(@RequestBody
@Validated(OnCreate.class) Persona persona) {
CONTROLLERS
Los controladores en Spring Boot son componentes fundamentales que actúan como la
capa de presentación en la aplicación. Son responsables de manejar las solicitudes
entrantes del usuario, procesarlas y devolver las respuestas adecuadas. Aquí está
el desglose de su función y cómo se utilizan:
Punto de Entrada:
· Los controladores son la puerta de entrada para las solicitudes HTTP que llegan a
su aplicación. Definen puntos de acceso, también conocidos como endpoints, que los
clientes pueden invocar.
Anotación @RestController:
· En Spring Boot, una clase de controlador se anota con @RestController, indicando
que está lista para manejar las solicitudes web y que su retorno se debe tomar como
el cuerpo de la respuesta (no como una vista).
Mapeo de Solicitudes:
Manejo de Datos:
Validaciones:
Inyección de Dependencias:
Desacoplamiento:
Pruebas:
Respuestas HTTP:
//EJEMPLO
package edu.cesur.fullstack.controllers;
import java.net.URI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import edu.cesur.fullstack.model.BookDTO;
import edu.cesur.fullstack.services.BookService;
@RestController
@RequestMapping("/books") // "/books" es como lo buscaremos en el postman
public class BookRestController {
bookService.createBook(book);
//La ubicacion URI siempre la tendremos que poner si queremos crear algo.Será
siempre igual, copiar y pegar.
URI location = ServletUriComponentsBuilder
.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(book.getId())
.toUri();
return ResponseEntity.created(location).build();
package edu.cesur.fullstack.controllers;
import java.net.URI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import edu.cesur.fullstack.services.ReservationService;
@RestController
@RequestMapping("/reservations")
public class ReservationRestController {
@Autowired
ReservationService reservationService;
@PostMapping("/{bookId}/{userId}")
public ResponseEntity<?> reserveBook(@PathVariable Integer bookId,
@PathVariable Integer userId){
reservationService.reserveBook(bookId, userId);
return ResponseEntity.created(location).build();
@GetMapping()
public ResponseEntity<?> getAll(){
return ResponseEntity.ok(reservationService.getAllReservations());
}
@PatchMapping("/books/{bookId}/users/{userId}")
public ResponseEntity<?> cancelReservation(@PathVariable Integer bookId,
@PathVariable Integer userId){
reservationService.cancelReservation(bookId, userId);
return ResponseEntity.noContent().build();
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.Scanner;
String cadena="";
while(!cadena.equalsIgnoreCase("fin")) {
System.out.println("Proceso Finalizado");
} catch (IOException e) {
e.printStackTrace();
}
}
}
//clase hijo (donde recogerá la informacion del padre y se hará todo la lógica y
devolverá informacion para que reciba el padre)
package Tarea2;
import java.util.Scanner;
while(cadena!= null) {
if(!cadena.equalsIgnoreCase("fin")){
(IMPORTANTE!!)
Una vez hecha la logica padre e hijo, tenemos que crearnos el archivo .jar que hace
de puente entren los dos.
Para ello: --> File --> export --> Java --> Runnable JAR file --> Launch
configutation: Hijo_NumAleatorios - PracticaProcesos (Aqui elegimos la clase hijo)
--> Export destination: C:\Prueba\numerosaleatorios.jar (elegimos la ubicacion y
nombre del archivo .jar que definimos en la clase padre) --> Finish
Clases de Caracteres:
[a-z]: Coincide con cualquier letra minúscula.
[A-Z]: Coincide con cualquier letra mayúscula.
[0-9]: Coincide con cualquier dígito.
[a-zA-Z]: Coincide con cualquier letra, tanto minúscula como mayúscula.
Caracteres Especiales:
.: Coincide con cualquier carácter excepto el salto de línea.
\d: Coincide con un dígito (equivalente a [0-9]).
\w: Coincide con un carácter alfanumérico o guion bajo.
\s: Coincide con un espacio en blanco (espacio, tabulación, salto de línea,
etc.).
Cuantificadores:
*: Coincide con cero o más repeticiones del elemento anterior.
+: Coincide con una o más repeticiones del elemento anterior.
?: Coincide con cero o una repetición del elemento anterior.
{n}: Coincide con exactamente n repeticiones del elemento anterior.
{n,}: Coincide con al menos n repeticiones del elemento anterior.
{n, m}: Coincide con entre n y m repeticiones del elemento anterior.
Anclajes:
^: Coincide con el inicio de una cadena.
$: Coincide con el final de una cadena.
Grupos y Alternativas:
(abc): Crea un grupo de caracteres, que coincide con "abc".
a|b: Coincide con "a" o "b".
Escape de Caracteres:
\\: Para escapar caracteres especiales. Por ejemplo, \\. coincide con un
punto literal.
Modificadores:
i: Realiza una coincidencia sin distinción entre mayúsculas y minúsculas.
g: Realiza una coincidencia global, buscando todas las ocurrencias en lugar
de detenerse en la primera.