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 + "]";
	}

	
}

Seguimos con DAO y más

Añado la clase DAO en un paquete aparte

package com.trifulcas.DAO;

import java.util.List;

import org.hibernate.Session;
import org.hibernate.Transaction;

import com.trifulcas.hibernate.HibernateUtil;

public class DAO<T> {
	private Class<T> entityClass;
	public Session session;

	public DAO(Class<T> clase) {
		this.entityClass = clase;
		session = HibernateUtil.getSessionFactory().openSession();
	}

	public void save(T Objeto) {
		Transaction transaction = null;
		try {
			transaction = session.beginTransaction();
			session.persist(Objeto);
			// commit transaction
			transaction.commit();
		} catch (Exception e) {
			if (transaction != null) {
				transaction.rollback();
			}
			e.printStackTrace();
		}
	}

	public List<T> getAll() {
		return session.createQuery("from " + entityClass.getSimpleName(), entityClass).list();

	}

	public T get(int id) {
		return session.get(entityClass, id);
	}

	public boolean delete(int id) {
		Transaction transaction = null;
		try {
			transaction = session.beginTransaction();
			
			T objeto = session.get(entityClass, id);
			session.remove(objeto);
			transaction.commit();
			return true;
		} catch (Exception ex) {
			System.out.println(ex.getMessage());
			if (transaction != null) {
				transaction.rollback();
			}
			return false;
		}
	}
}

Creo la entidad Libro

package com.trifulcas.models;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;

@Entity
public class Libro {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "idlibro")
	private int idLibro;
	private String titulo;
	private int paginas;
	@ManyToOne
	@JoinColumn(name="idgenero")
	private Genero genero;
	
	public Libro() {
		super();
		// TODO Auto-generated constructor stub
	}
	public int getIdLibro() {
		return idLibro;
	}
	public void setIdLibro(int idLibro) {
		this.idLibro = idLibro;
	}
	public String getTitulo() {
		return titulo;
	}
	public void setTitulo(String titulo) {
		this.titulo = titulo;
	}
	public int getPaginas() {
		return paginas;
	}
	public void setPaginas(int paginas) {
		this.paginas = paginas;
	}
	
	public Genero getGenero() {
		return genero;
	}
	public void setGenero(Genero genero) {
		this.genero = genero;
	}
	@Override
	public String toString() {
		return "Libro [idLibro=" + idLibro + ", titulo=" + titulo + ", paginas=" + paginas + "]";
	}
	
}

Añado a Genero la anotación onetomany

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;
	}

	@Override
	public String toString() {
		return "Genero [idGenero=" + idGenero + ", nombre=" + nombre + "]";
	}

	
}

Lo anoto en hibernateutil

...
				configuration.addAnnotatedClass(Libro.class);

...

Y lo pruebo

package com.trifulcas.Biblioteca;

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

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args )
    {
    	try {
			DAO<Genero> gdao=new DAO<>(Genero.class);
			Genero g=gdao.get(1);
			System.out.println(g);
			System.out.println(g.getLibros());
			DAO<Libro> ldao=new DAO<>(Libro.class);
			Libro l=ldao.get(2);
			System.out.println(l);
			System.out.println(l.getGenero());

			/*
			 * Genero sf=new Genero();
			
			sf.setNombre("Ciencia ficción");
			gdao.save(sf);
			 */
			//gdao.delete(6);
		} catch (Exception ex) {
			System.out.println(ex);
		}
    	
    }
}

Añado autor, pongo el manytomany y modifico libro para que también lo tenga

package com.trifulcas.models;

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

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

@Entity
@Table(name = "autor")
public class Autor {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int idautor;

	private String nombre;

	@ManyToMany(cascade= {CascadeType.MERGE})
	@JoinTable(name = "libro_autor", joinColumns = { @JoinColumn(name = "idautor") }, // La de esta entidad
			inverseJoinColumns = { @JoinColumn(name = "idlibro") })
	List<Libro> libros = new ArrayList();

	// Constructor por defecto
	public Autor() {
	}

	// Constructor con parámetros
	public Autor(String nombre) {
		this.nombre = nombre;
	}

	// Getters y Setters
	public int getIdautor() {
		return idautor;
	}

	public void setIdautor(int idautor) {
		this.idautor = idautor;
	}

	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;
	}

	// Método toString
	@Override
	public String toString() {
		return "Autor{" + "idautor=" + idautor + ", nombre='" + nombre + '\'' + '}';
	}
}
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.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;

@Entity
public class Libro {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "idlibro")
	private int idLibro;
	private String titulo;
	private int paginas;
	
	@ManyToMany(mappedBy="libros")
	List<Autor> autores=new ArrayList();
	@ManyToOne
	@JoinColumn(name="idgenero")
	private Genero genero;
	
	public Libro() {
		super();
		// TODO Auto-generated constructor stub
	}
	public int getIdLibro() {
		return idLibro;
	}
	public void setIdLibro(int idLibro) {
		this.idLibro = idLibro;
	}
	public String getTitulo() {
		return titulo;
	}
	public void setTitulo(String titulo) {
		this.titulo = titulo;
	}
	public int getPaginas() {
		return paginas;
	}
	public void setPaginas(int paginas) {
		this.paginas = paginas;
	}
	
	public Genero getGenero() {
		return genero;
	}
	public void setGenero(Genero genero) {
		this.genero = genero;
	}
	
	public List<Autor> getAutores() {
		return autores;
	}
	public void setAutores(List<Autor> autores) {
		this.autores = autores;
	}
	@Override
	public String toString() {
		return "Libro [idLibro=" + idLibro + ", titulo=" + titulo + ", paginas=" + paginas + "]";
	}
	
}

Lo pruebo:

package com.trifulcas.Biblioteca;

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

/**
 * Hello world!
 *
 */
public class App {
	public static void main(String[] args) {
		try {
			DAO<Genero> gdao = new DAO<>(Genero.class);
			Genero g = gdao.get(1);
			System.out.println(g);
			System.out.println(g.getLibros());
			DAO<Libro> ldao = new DAO<>(Libro.class);
			Libro l = ldao.get(2);
			System.out.println(l);
			System.out.println(l.getGenero());
			DAO<Autor> adao = new DAO<>(Autor.class);
			Autor a = adao.get(2);
			System.out.println(a);
			System.out.println(a.getLibros());
			
			Libro fantasia=ldao.get(7);
			
			// Añado el libro al autor
			a.getLibros().add(fantasia);
			// Lo guardo
			adao.save(a);
			/*
			 * Genero sf = new Genero();
			 * 
			 * sf.setNombre("Fantasía romántica"); 
			 * gdao.save(sf);
			 * Libro libro=new Libro();
			 * libro.setTitulo("Amor en el fin del mundo"); 
			 * libro.setPaginas(200);
			 * libro.setGenero(sf); 
			 * ldao.save(libro);
			 */
			
		} catch (Exception ex) {
			System.out.println(ex);
		}

	}
}