RestController Parcial

package com.trifulcas.SpringBootAPI.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.trifulcas.SpringBootAPI.model.Category;
import com.trifulcas.SpringBootAPI.repository.CategoryRepository;

@RestController
@RequestMapping("/category")
public class CategoryController {

	@Autowired
	CategoryRepository categoryRepository;
	
	@GetMapping("")
	public List<Category> getAll() {
		return categoryRepository.findAll();
	}

	// Poner los valores en la URL, no parámetros nombrados
	@GetMapping("/{id}")
	public Category getById(@PathVariable int id) {
		System.out.println(id);
		return categoryRepository.findById(id).orElse(null);
	}
	
	@PostMapping("")
	public Category add(@RequestBody Category cat) {
		System.out.println(cat);
		if (categoryRepository.existsById(cat.getCategoryId())) {
			return null;
		}
		return categoryRepository.save(cat);
	}

	@PutMapping("/put")
	public String put() {
		return "put";
	}

	@PatchMapping("/patch")
	public String patch() {
		return "patch";
	}

	@DeleteMapping("/delete")
	public String delete() {
		return "delete";
	}
}

Ejemplo map verbos en Controller

package com.trifulcas.SpringBootAPI.controller;

import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class CategoryController {

	@GetMapping("/get")
	public String get() {
		return "get";
	}

	@PostMapping("/post")
	public String post() {
		return "post";
	}

	@PutMapping("/put")
	public String put() {
		return "put";
	}

	@PatchMapping("/patch")
	public String patch() {
		return "patch";
	}

	@DeleteMapping("/delete")
	public String delete() {
		return "delete";
	}
}

Organización de los elementos en una app Spring

Cuando utilizamos la anotación @SpringBootApplication spring boot busca los elementos habituales tales como controladores, repositorios o entidades.
Si están en subpaquetes del paquete raíz no hay problema, los encuentra automáticamente

Elemento raíz

package com.trifulcas.SpringBootData

Subpaquetes

package com.trifulcas.SpringBootData.model;

Si no pasa así le tenemos que decir al springboot donde está cada elemento. Si no, nos dará error.

@SpringBootApplication
// Esta anotación nos dice donde buscar los controladores
@ComponentScan("com.trifulcas")
// Esta donde buscar las entidades
@EntityScan("com.trifulcas.models")
// Donde buscar los repositorios
@EnableJpaRepositories("com.trifulcas.repository")
public class SpringBoot04Application {
[...]

Country

Entidad

package com.trifulcas.SpringBootData;

import java.sql.Timestamp;
import java.util.Date;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

@Entity
@Table(name = "country")
public class Country {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "country_id")
	private int countryId;

	@Column(name = "country", nullable = false)
	private String country;

	@Column(name = "last_update", nullable = false)
	private Timestamp lastUpdate;

	
	public Country() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Country(String country) {
		super();
		this.country = country;
		Date now = new Date();
		this.lastUpdate = new Timestamp(now.getTime());
	}

	public int getCountryId() {
		return countryId;
	}

	public void setCountryId(int countryId) {
		this.countryId = countryId;
	}

	

	public String getCountry() {
		return country;
	}

	public void setCountry(String country) {
		this.country = country;
	}

	public Timestamp getLastUpdate() {
		return lastUpdate;
	}

	public void setLastUpdate(Timestamp lastUpdate) {
		this.lastUpdate = lastUpdate;
	}

	@Override
	public String toString() {
		return "Country [countryId=" + countryId + ", name=" + country + ", lastUpdate=" + lastUpdate + "]";
	}

}

Repositorio

package com.trifulcas.SpringBootData;

import org.springframework.data.repository.CrudRepository;

public interface CountryRepository extends CrudRepository<Country, Integer> {

}

Controlador

package com.trifulcas.SpringBootData;

import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

// Esta anotación serializa por defecto todo lo que las funciones retornen
@RestController
@RequestMapping("/country") // Esto permite poner un prefijo a todas las peticiones
public class CountryController {

	// Lo que hace es crear e inyectar un objeto de tipo
	// CRUD con la entidad 'Country'
	// Este objeto nos servirá como un DAO, nos permite crear, modificar
	// obtener y borrar cualquier elemento de la web
	// el objeto repository nos permite usar findall, findbyid, deletebyid, save...
	@Autowired
	private CountryRepository countryRepository;

	@GetMapping("/add")
	public Country addNewCountry(@RequestParam(value = "name", defaultValue = "Nueva categoría") String name) {
		Country cat = new Country(name);
		countryRepository.save(cat);
		return cat;
	}

	@GetMapping("/delete")
	public String deleteCountry(@RequestParam(value = "id", defaultValue = "0") String id) {
		try {
			countryRepository.deleteById(Integer.parseInt(id));
			return "Borrado " + id;
		} catch (Exception ex) {
			return ex.getMessage();
		}
	}

	@GetMapping("/get")
	public Optional<Country> getCountry(@RequestParam(value = "id", defaultValue = "0") String id) {
		try {
			Optional<Country> cat = countryRepository.findById(Integer.parseInt(id));
			return cat;
		} catch (Exception ex) {
			return Optional.empty();
		}
	}

	@GetMapping("/all")
	public Iterable<Country> viewAll() {
		return countryRepository.findAll();
	}
}

Como gestionar tipos optional

El tipo optional es un nuevo tipo que permite gestionar búsquedas en la base de datos que no devuelven un valor. Nos da más información que simplemente tener null.

https://www.baeldung.com/java-optional-return

¿Cómo gestionarlo, por ejemplo, en un controlador?

Opción a, convertir a nulo una búsqueda sin resultado

@GetMapping("/get")
	public Category getCategory(@RequestParam(value = "id", defaultValue = "0") String id) {
		try {
			Category cat=categoryRepository.findById(Integer.parseInt(id)).orElse(null);
			
			return cat;
		} catch (Exception ex) {
			return null;
		}
	}

Opción b, tratarlo como Optional

@GetMapping("/get")
	public Optional<Category> getCategory(@RequestParam(value = "id", defaultValue = "0") String id) {
		try {
			Optional<Category> cat=categoryRepository.findById(Integer.parseInt(id));
			
			return cat;
		} catch (Exception ex) {
			return Optional.empty();
		}
	}

En nuestro caso no hay problema porque el serializador de Spring lo trata bien.

Crear un modelo-controlador

Para hacer con Spring Boot un Modelo – Controlador, es decir, que podamos acceder a una base de datos y mostrar información tenemos que hacer los siguientes pasos:

1.- Crear un proyecto con las dependencias SpringBoot, JPA y Mysql

2.- Configurar el acceso a la base de datos:
application.properties

spring.jpa.hibernate.ddl-auto=none
spring.datasource.url=jdbc:mysql://localhost:3306/sakila
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.show-sql: true

3.- Crear una entidad, igual que hacía en hibernate

package com.trifulcas.SpringBootData;

import java.sql.Timestamp;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import jakarta.persistence.*;

@Entity
@Table(name = "category")
public class Category {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "category_id")
	private int categoryId;

	@Column(name = "name", nullable = false)
	private String name;

	@Column(name = "last_update", nullable = false)
	private Timestamp lastUpdate;

	
	public Category() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Category(String name) {
		super();
		this.name = name;
		Date now = new Date();
		this.lastUpdate = new Timestamp(now.getTime());
	}

	public int getCategoryId() {
		return categoryId;
	}

	public void setCategoryId(int categoryId) {
		this.categoryId = categoryId;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Timestamp getLastUpdate() {
		return lastUpdate;
	}

	public void setLastUpdate(Timestamp lastUpdate) {
		this.lastUpdate = lastUpdate;
	}

	@Override
	public String toString() {
		return "Category [categoryId=" + categoryId + ", name=" + name + ", lastUpdate=" + lastUpdate + "]";
	}

}

4.- Crear un ‘repository’ para la entidad. ¿Qué es esto? Es una especie de DAO pero ya prefabricado.¡OJO! Es un interface, no una clase.

public interface CategoryRepository extends CrudRepository<Category, Integer> {

}

5.- Por último podremos usar todas las utilidades del repository en cualquier controlador. Para hacerlo tenemos que definir una clase del mismo tipo que el repository y anotarla con @AutoWired

package com.trifulcas.SpringBootData;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

// Esta anotación serializa por defecto todo lo que las funciones retornen
@RestController
public class CategoryController {
	
	// Lo que hace es crear e inyectar un objeto de tipo
	// CRUD con la entidad 'Category'
	// Este objeto nos servirá como un DAO, nos permite crear, modificar
	// obtener y borrar cualquier elemento de la web
	// el objeto repository nos permite usar findall, findbyid, deletebyid, save...
	@Autowired
	private CategoryRepository categoryRepository;

	@GetMapping("/add")
	public String addNewCategory(
			@RequestParam(value = "name", defaultValue = "Nueva categoría") 
			String name) {
		Category cat = new Category(name);
		categoryRepository.save(cat);
		return "Saved "+cat.getCategoryId();
	}
	
	@GetMapping("/all")
	public Iterable<Category> viewAll() {
		return categoryRepository.findAll();
	}

@GetMapping("/delete")
	public String deleteCategory(
			@RequestParam(value = "id", defaultValue = "0") 
			String id) {
		try {
			categoryRepository.deleteById(Integer.parseInt(id));
			return "Borrado "+id;
		}catch(Exception ex) {
			return ex.getMessage();
		}
	}
	
}

Solución ejercicio

package com.trifulcas.controller;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class EjercicioController {
	// Endpoint. Cualquier programa que acceda a este endpoint
	// Tendrá esta respuesta
	// url del servidor + '/ola'
	@GetMapping("/ola")
	public String ola() {
		return "Ola k ase???";
	}
	
	@GetMapping("/normal")
	public String normal() {
		return "Hola ¿Qué tal?";
	}

	@GetMapping("/personal")
	public String personal(
			@RequestParam(value = "nombre", defaultValue = "Anónimo")
			String nombre) {
		return String.format("Hola %s ¿Cómo estás?", nombre);
	}

	@GetMapping("/azar")
	public String azar() {
		return azarRandom();
	}
	private String azarRandom() {
		List<String> mensajes = new ArrayList<>(Arrays.asList("Hola", "Que hay?", "Buenas", "Jelou", "Hey"));
		Random r=new Random();
		return mensajes.get(r.nextInt(mensajes.size()));
	}
}

Spring boot

Asó queda el main

package com.trifulcas.TestSpringBoot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan("com.trifulcas")
public class TestSpringBootApplication {

	public static void main(String[] args) {
		SpringApplication.run(TestSpringBootApplication.class, args);
	}

}

Así queda el controlador

package com.trifulcas.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
	// Dentro del controlador pondré todas las entradas a mi app
	// Se mapean con Mapping y la ruta
	@GetMapping("/")
	public String hello(@RequestParam(value = "name", defaultValue = "World") String name) {
		return String.format("Hello %s!", name);
	}
}

Controlador con más endpoints

package com.trifulcas.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
	// Dentro del controlador pondré todas las entradas a mi app
	// Se mapean con Mapping y la ruta
	// Defino un punto de entrada que es la raiz
	@GetMapping("/")
	public String hello(@RequestParam(value = "name", defaultValue = "World") String name) {
		System.out.println("Han entrado en la raiz. Name vale"+name);
		return String.format("Hello %s!", name);
	}
	// mi punto de entrada es /pepe
	@GetMapping("/pepe")
	public String pepe() {
		System.out.println("Han entrado en pepe");
		return "Me llamo pepe";
	}
	// Punto de entrada /suma y recupero dos parámetros (a y b) con los que hago la suma
	@GetMapping("/suma")
	public String suma(@RequestParam(value = "a", defaultValue = "1") String a, @RequestParam(value = "b", defaultValue = "1") String b) {
		System.out.println("Han entrado en suma con valores "+a+","+b);
		return (Integer.parseInt(a)+Integer.parseInt(b))+"";
	}
}

Ejemplos HQL

	Session session = HibernateUtil.getSessionFactory().openSession();
		// Ojo aquí, el from es caseinsensitive pero la entidad 'Autor'
		// SI LO ES tiene que ser como se llame la entidad
		// Esto es lo más sencillo, seleccionar todo de una entidad
		List<Autor> autores = session.createQuery("from Autor", Autor.class).list();
		System.out.println(autores);

		// Podemos añadir condiciones con Where
		autores = session.createQuery("from Autor  where idautor=6", Autor.class).list();
		System.out.println(autores);
		// Podemos seleccionar algunas propiedades
		List<String> nombres = session.createQuery("select nombre from Autor", String.class).list();
		System.out.println(nombres);
		
		// Podemos usar parámetros
		Query<Autor> query = session.createQuery("from Autor where idautor=:id", Autor.class);
		query.setParameter("id",6);
		autores=query.list();
		System.out.println(autores);
		
		// Si nos fijamos después de cada query ponemos list()
		// Esto nos sirve para dos cosas: obtener una lista
		// Y, sobre todo, ejecutar la query.
		// Las queries son como los procedimientos preparados
		// No se lanzan hasta que se lo decimos

Ejercicio de trabajar con Hibernate

Por partes.
Crear un género:

Genero g=new Genero();
g.setNombre("SteamPunk");
		DAO<Genero> gdao=new DAO<>(Genero.class);
		gdao.save(g);

Crear un libro:

DAO<Genero> gdao=new DAO<>(Genero.class);
		Genero g=gdao.get(8);
		Libro l=new Libro();
		l.setTitulo("Luna cruel");
		l.setPaginas(750);
		l.setGenero(g);
	DAO<Libro> ldao=new DAO<>(Libro.class);
	
		ldao.save(l);

Crear un autor

DAO<Libro> ldao=new DAO<>(Libro.class);
		Libro l=ldao.get(9);
		
		DAO<Autor> adao=new DAO<>(Autor.class);
		Autor a=new Autor();
		a.setNombre("Juan Perez");
		a.getLibros().add(l);
		adao.save(a);

Todo junto

	// Todo junto
		DAO<Genero> gdao=new DAO<>(Genero.class);
		DAO<Libro> ldao=new DAO<>(Libro.class);
		DAO<Autor> adao=new DAO<>(Autor.class);
		
		// Creo el género
		Genero g=new Genero();
		g.setNombre("Realismo sucio");
		gdao.save(g);
		
		// Creo el libro
		Libro l=new Libro();
		l.setTitulo("Cloacas de Barcelona");
		l.setPaginas(750);
		// Le asigno el género que acao de crear
		l.setGenero(g);
		ldao.save(l);
		
		// Creo el autor
		Autor a=new Autor();
		a.setNombre("Andreu Martín");
		// Le añado el libro que acabo de crear
		a.getLibros().add(l);
		adao.save(a);

Autores por género.
Con un método:

package com.trifulcas.Biblioteca;

import java.util.ArrayList;
import java.util.List;

import com.trifulcas.DAO.DAO;
import com.trifulcas.models.Autor;
import com.trifulcas.models.Genero;
import com.trifulcas.models.Libro;

public class GetAutores {

	public static void main(String[] args) {
		DAO<Genero> gdao=new DAO<>(Genero.class);
		Genero g=gdao.get(8);
		System.out.println(getAutores2(g));
		System.out.println(g.getAutores());
	}

	public static List<Autor> getAutores(Genero genero){
		List<Autor> res=new ArrayList<>();
		for(Libro libro:genero.getLibros()) {
			for(Autor autor:libro.getAutores()) {
				res.add(autor);
			}
		}
		return res;
	}
	public static List<Autor> getAutores2(Genero genero){
		List<Autor> res=new ArrayList<>();
		for(Libro libro:genero.getLibros()) {
			res.addAll(libro.getAutores());
		}
		return res;
	}
}

Dentro del POJO (no recomendable pero… ya se sabe 😉 )

package com.trifulcas.models;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;

@Entity
public class Genero {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "idgenero")
	private int idGenero;

	private String nombre;

	@OneToMany(mappedBy="genero")
	List<Libro> libros=new ArrayList();
	
	public Genero() {
		super();
		// TODO Auto-generated constructor stub
	}

	public int getIdGenero() {
		return idGenero;
	}

	public void setIdGenero(int idGenero) {
		this.idGenero = idGenero;
	}

	public String getNombre() {
		return nombre;
	}

	public void setNombre(String nombre) {
		this.nombre = nombre;
	}

	public List<Libro> getLibros() {
		return libros;
	}

	public void setLibros(List<Libro> libros) {
		this.libros = libros;
	}
	public List<Autor> getAutores() {
		List<Autor> res=new ArrayList<>();
		for(Libro libro:this.getLibros()) {
			res.addAll(libro.getAutores());
		}
		return res;
	}
	@Override
	public String toString() {
		return "Genero [idGenero=" + idGenero + ", nombre=" + nombre + "]";
	}

	
}