1. ¿Para qué sirve?
  2. ORM
  3. Clave Primaria
  4. Instalación
  5. Transacciones
  6. Session
  7. Claves foráneas
  8. Ejercicio autor
  9. Criteria
  10. HQL
  11. Ejercicio global
  12. Crear una relación n a n
  13. Ejercicio hospital

¿Para qué sirve?

  • Disminuye la cantidad de código usado.
  • Simplifica la interacción con la base de datos.
  • Optimiza el acceso a la base de datos.

ORM

Object Relational Mapping. Se encarga de transformar las tablas en clases, los campos en propiedades y viceversa.

Clave primaria

Usando Hibernate:

  • es fundamental que las tablas tengan clave primaria.
  • no es posible modificar claves primarias.

Instalación

  1. Descargamos las librerías de Hibernate: http://hibernate.org/orm/releases/
  2. De entre los ficheros descargados, copio la carpeta lib en mi proyecto.
  3. Copio el driver de mysql en mi proyecto.
  4. Refresco el proyecto para que coja los cambios realizados.
  5. Añado al java build path del proyecto los jar que están dentro de la carpeta required, así como el driver MySQL.
  6. Creamos el fichero hibernate.cfg.xml en la carpeta src y le pegamos el siguiente código:
    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
    <hibernate-configuration>
    <session-factory>
    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="hibernate.connection.isolation">2</property>
    <property name="hibernate.connection.password">pp</property>
    <property name="hibernate.connection.pool_size">10</property>
    <property name="hibernate.connection.url">jdbc:mysql://localhost/prueba</property>
    <property name="hibernate.connection.username">root</property>
    <property name="hibernate.current_session_context_class">managed</property>
    <property name="hibernate.dialect">org.hibernate.dialect.MySQL57Dialect</property>
    <property name="hibernate.show_sql">true</property>
    <property name="hbm2ddl.auto">update</property>
    </session-factory>
    </hibernate-configuration>
  7. Creamos el fichero HibernateUtil.java para establecer la conexión
    public class HibernateUtil {
    	private static StandardServiceRegistry registry;
    	private static SessionFactory sessionFactory;
    
    	public static SessionFactory getSessionFactory() {
    		if (sessionFactory == null) {
    			try {
    				registry = new StandardServiceRegistryBuilder().configure().build();
    				MetadataSources sources = new MetadataSources(registry);
    				sources.addAnnotatedClass(Alumno.class);
    				Metadata metadata = sources.getMetadataBuilder().build();
    				sessionFactory = metadata.getSessionFactoryBuilder().build();
    			} catch (Exception e) {
    				e.printStackTrace();
    				if (registry != null) {
    					StandardServiceRegistryBuilder.destroy(registry);
    				}
    			}
    		}
    		return sessionFactory;
    	}
    }
  8. Creamos el paquete entity (pojos de hibernate) y una clase de prueba
    import javax.persistence.Entity;
    import javax.persistence.Id;
    
    @Entity
    public class Alumno {
    	@Id
    	@GeneratedValue(strategy=GenerationType.IDENTITY)//Para generar números autoincrementados
    	private int id;
    	private String nombre;
    	private int edad;
    
    	//Tener en cuenta que si definimos un constructor, este debe ser público
    	public int getId() {
    		return id;
    	}
    
    	public void setId(int id) {
    		this.id = id;
    	}
    
    	public String getNombre() {
    		return nombre;
    	}
    
    	public void setNombre(String nombre) {
    		this.nombre = nombre;
    	}
    
    	public int getEdad() {
    		return edad;
    	}
    
    	public void setEdad(int edad) {
    		this.edad = edad;
    	}
    
    }

Errores de puertos

Si al arrancar el servidor de apache y el servidor de mysql tuviese algún conflicto de puertos...

  • Debo comprobar aplicaciones en la barra de tareas que sean susceptibles de estar usando alguno de los puertos que necesito (Mysql Monitor, etc.)
  • En windows, ejecutando services.msc puedo ver todos los servicios que se están ejecutando en el sistema. Debo detener (si estuviesen) los de:
    • VM Ware
    • Servicio de Publicación World Wide Web
    • Skype

Transacciones

Conjunto de operaciones contra la base de datos que se realizan de forma atómica (o todas o ninguna).

¿Cuando nos interesa crear una transacción? Cuando modificamos la base de datos (y por tanto deseamos poder hacer rollback()). Para una consulta no es necesario. Crear una transacción consume recursos.

  • Un objeto Session Hibernate representa una única unidad-de-trabajo y es abierta por un ejemplar de SessionFactory. Se deben cerrar las sesiones cuando se haya completado todo el trabajo de una transación.
  • En caso de fallo, lo que hay dentro del beginTransaction y el commit no se ejecuta y se salta a un catch.
  • Siempre es más rápido hacer dos operaciones en una sola transacción que dos operaciones en dos transacciones.
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction transaction = session.beginTransaction();
Alumno al = new Alumno();
al.setNombre("Juan");
al.setEdad(10);
		
session.save(al);
transaction.commit();
session.close();

sesion.beginTransaction().setTimeout(400);

  • Si pasados 400 milisegundos la base de datos no devuelve nada, se arrojará una UnCaught Eception (una excepción para la que no es estrictamente neceario tener un try catch; al contrario que las Caught Exception).
  • Este timeout se puede gestionar a nivel de aplicación o a nivel de base de datos.

Métodos de la clase Session

session.save(persona) // guarda un objeto como registro en la base de datos.
Session.refresh(persona) // en nos permitirá conocer la id del objeto insertado, suponiendo que la id sea autoincrementada.
session.update(persona);
session.saveOrUpdate(persona);
session.get(Persona.class, id) // me permite recuperar datos.
// el método load lo usamos sólo para borrar un paciente. ya que nos devuelve un paciente sólo con la clave primaria seteada. Para recuperar todos los valores usaremos get.
Persona personaABorrar = (Persona) session.load(Persona.class, idPaciente);
session.delete(personaABorrar);

Definir claves foráneas con sql

El siguiente código SQL genera una relación 1 a muchos:


ALTER TABLE libro ADD CONSTRAINT fk_autor FOREIGN KEY ( id_autor ) REFERENCES autor( id )

El código para generar una relación uno a uno sería igual, pero cambiando los nombres de los campos a los que hago referencia:

ALTER TABLE autor ADD FOREIGN KEY ( id ) REFERENCES padre_autor (id)

Nota: Para asignar las foreing keys es necesario que:

  • los tipos de datos de los campos relacionados coincidan.
  • Las tablas deberían estar vacías, u obtendremos un error del tipo "Table already exists".
  • El motor de las tablas sea InnoDB.

diagrama hibernate

Carga perezosa

Es una estrategia que consiste en demorar la carga de un objeto hasta que este sea requerido.

Es decir, la carga del objeto se realiza de manera explícita cuando este es invocado.

Lazy loading se utiliza en aquellos casos en los que la aplicación necesita acceder solo a una parte de un objeto.

En general la carga perezosa sólo se utiliza en las colecciones 1-n y n-n. Está por defecto a lazy true. Es posible usar cargar perezosas en relaciones 1-1 pero no es sencillo ni frecuente.

Ejercicio autor

1 - establecer la relación 1-n en los pojos

@Entity
public class Autor {
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)	
	private int id;

	private String nombre;
	
	@OneToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER, mappedBy="autor") //eager = carga ansiosa
	private List<Libro> libros;
@Entity
public class Libro {
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private int id;
    
	private String titulo;

	@ManyToOne
	private Autor autor;

2 - Insertar un autor

Session session = HibernateUtil.getSessionFactory().openSession();
Transaction transaction = session.beginTransaction();
Autor autor = new Autor();
autor.setNombre("Juan");
		
session.save(autor);
transaction.commit();
session.close();

3 - Insertar un autor y sus libros

Session session = HibernateUtil.getSessionFactory().openSession();
Transaction transaction = session.beginTransaction();
Autor autor = new Autor();
List<Libro> libros = new ArrayList<Libro>();
libros.add(new Libro(autor, "titulo1"));
libros.add(new Libro(autor, "titulo2"));
autor.setNombre("Juan");
autor.setLibros(libros);
session.save(autor);
transaction.commit();
session.close();

session.flush()

Este método se asegura de que la información ha sido persistida en la base de datos, de forma que ya sea posible cerrar con seguridad la session.

Este método es ejecutado automáticamente por session.close(), no obstante es recomendable que siempre lo ejecutemos explícitamente justo antes.

Puede ser útil hacer un flush para recuperar la id autoincrementada de un objeto recien insertado. Ejemplo:

session.save(persona);
session.flush();
id = persona.getId();

Criteria

Se utiliza para hacer consultas

Métodos de la clase Criteria

Session session = HibernateUtil.getSessionFactory().openSession();
		
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Autor> criteria = builder.createQuery(Autor.class);
Root<Autor> root = criteria.from(Autor.class);
criteria.select(root).where(builder.equal(root.get("nombre"), "Juan"));
TypedQuery<Autor> q=session.createQuery(criteria);
		
//List<Autor> list = q.getResultList();
Autor cicloResultado = q.getSingleResult();

Nota: Utilizando Criteria no es necesario hacer commit(), ya que la llamada a este método es para materializar cambios en la base de datos.

Ejercicio 5 libros

Insertar 5 libros en la base de datos. Dos registros deben tener el mismo título (''titulo1'').

Utilizar la clase criteria para mostrar los libros cuyo título contenga la palabra ''titulo1''. Así es como ejecutaremos una sentencia de tipo like:

criteria.select(root).where(builder.like(root.get("titulo"), "%titulo1%"));

Hacer una nueva consulta, ordenando la salida por el título, e ir recorriéndola imprimiendo los títulos y las id con System.out.println();

criteria.orderBy(builder.asc(root.get("titulo")));

ID:1 Título:titulo1
ID:5 Título:titulo1
ID:6 Título:titulo2
ID:2 Título:titulo3
ID:4 Título:titulo4
ID:3 Título:titulo5

HQL

Definir consultas
//recupero una colección de objetos
Query<Libro> hqlQuery1 = session.createQuery("FROM  Libro");
//recupero un String en vez de un objeto
Query<String> hqlQuery2 = session.createQuery("SELECT v.matricula FROM Vehiculo v");
//Consulta ordenada
Query<Libro> hqlQuery3 = session.createQuery("FROM Libro ORDER BY id");
//Consulta con condiciones
Query<Libro> hqlQuery4 = session.createQuery("FROM Libro WHERE precio>10 AND anio_publicacion>1984");

Ejecutar consulta
List<Libro> libros = hqlQuery.list();
Libro libro = hqlQuery.getSingleResult();

Eliminar un registro
Query hqlQuery = session.createQuery("DELETE FROM Paciente WHERE id=(:condicion)");
hqlQuery.setParameter("condicion", 4);
hqlQuery.executeUpdate();

Evitar inyección SQL

Se debe evitar crear consultas HQL concatenando Strings:
String queryString = "from item i where i description like ' " + search + " ' ";


En cambio es aconsejable usar:
String queryString = "FROM Libro WHERE titulo LIKE (:condicion)";
hqlQuery.setParameter("condicion", "%tit%");
List result2 = hqlQuery2.list();

Consulta relacionada


Evaluar si una propiedad del bean es igual a cierto valor
(no funciona si la propiedad es una lista)
String queryString = "FROM Libro l WHERE l.autor.nombre LIKE (:condicion)";

Evaluar si uno de los los elementos de la propiedad del bean consultado es igual a cierto valor
String queryString = " SELECT a FROM Autor a JOIN a.libros l WHERE l.titulo='titulox11'";
Query hqlQuery = session.createQuery(queryString);

Nota: La siguiente línea es equivalente a la línea en verde (pero utilizando join):


String queryString = "select l from Libro l join l.autor a where a.nombre='nombre1'";

Las palabras escritas en rojo son propiedades de las clases, no son campos de la base de datos.

Ejercicio

Dada la siguiente estructura:

diagrama ejercicio siniestro vehículo

Ejecutar las siguientes consultas

  1. Listar las matrículas de todos los vehículos.
  2. Listar la matrícula y la marca de todos los vehículos ordenados por número de ruedas.
  3. Listar la matrícula de todos los vehículos que tengan más de dos asientos y más de dos ruedas.
  4. Listar la matrícula de todos los vehículos que tengan más de dos asientos o más de dos ruedas.
  5. Listar la matrícula de todos los vehículos que tengan una matrícula de menos de seis caracteres (where length(v.matricula)<?).
  6. Listar la matrícula de todos los vehículos que hayan tenido un siniestro con perdida mayor de 1000 euros.
  7. Listar la matrícula de todos los vehículos que hayan tenido un siniestro con perdida mayor de 1000 euros. Mostrar a cuanto ascendió la pérdida.
    Almacenar el resultado de la la consulta en:
    • Una lista de listas de dos elementos; el primero será un bean de Vehículo y el segundo un Integer. La siguiente línea me devuelve una lista de listas de dos elementos; el primero será un bean y el segundo un String.
      select new List(v, s.perdida) from Vehiculo... 
    • Una lista de beans VehiculoSiniestro, que tendrán cada uno dos propiedades, Vehiculo e Integer. Habrá que crear la clase VehiculoSiniestro con su correspondiente constructor.
      select new com.pablomonteserin.main.VehiculoSiniestro(v, s.perdida)...

    Cómo no resolver este ejercicio:

    select v.matricula, v.marca from Vehiculo v order by v.ruedas

    Si ejecutamos esta consulta y pretendemos recorrerla, estaríamos recorriendo una lista de un array de Objects:

    List <Object[]>result = hqlQuery.list();
    Iterator<Object[]> it = result.iterator();
    while(it.hasNext()){
    	Object[] obj =  it.next();				 
    	System.out.println("Matricula: "+ obj[0]);
    	System.out.println("Marca: "+ obj[1]);

Ejercicio

Dada la siguiente estructura:

diagrama ejercicio profesor curso

Ejecutar las siguientes consultas

  1. Listar los nombres de todos los profesores.
  2. Listar el nombre y el apellido de todos los profesores ordenados por edad.
    Cómo no resolver este ejercicio:
    select p.nombre, p.apellido from Profesor v order by p.edad

    Si ejecutamos esta consulta y pretendemos recorrerla, estaríamos recorriendo una lista de un array de Objects:

    List <Object[]>result = hqlQuery.list();
    Iterator<Object[]> it = result.iterator();
    while(it.hasNext()){
    	Object[] obj =  it.next();				 
    	System.out.println("Nombre: "+ obj[0]);
    	System.out.println("Apellido: "+ obj[1]);
  3. Listar el nombre de todos los profesores que tengan más de treinta años y tengan más de 5 años de experiencia.
  4. Listar el nombre de todos los profesores que tengan más de treinta años o tengan más de 5 años de experiencia.
  5. Listar el nombre de todos los profesores que tengan un nombre de menos de seis caracteres (where length(p.nombre)<?).
  6. Listar el nombre de todos los profesores que hayan impartido un curso de más de 500 euros.
  7. Listar el nombre de todos los profesores que hayan impartido un curso de más de 500 euros. Mostrar cuanto ćostó dicho curso.
    Almacenar el resultado de la la consulta en:
    • Una lista de listas de dos elementos; el primero será un bean de Vehículo y el segundo un Integer. La siguiente línea me devuelve una lista de listas de dos elementos; el primero será un bean y el segundo un String.
      select new List(p, c.precio) from Profesor... 
    • Una lista de beans ProfesorCurso, que tendrán cada uno dos propiedades, Profesor e Integer. Habrá que crear la clase ProfesorCurso con su correspondiente constructor.
      select new com.pablomonteserin.main.ProfesorCurso(p, c.precio)...

Crear una relación n a n

Una relación n a n son dos relaciones 1 a n vinculadas a una misma tabla auxiliar. En MySQL WorkBench, podemos establecer dicha relación desde el panel Models → flechita → Create EER Model from Database, de forma que se creará automáticamente la tabla intermedia.

Ejercicio hospital

La aplicación consta de 3 clases: Main, Alumno y Servicio.

La clase Alumno tendrá tres propiedades: id(int), nombre (String) y edad (int).

Al arrancar la aplicación desde el Main se nos preguntará por la operación que deseamos hacer:

  • Insertar un nuevo alumno. Esta opción nos solicita el nombre y la edad del alumno que queremos insertar.
  • Eliminar un alumno. Esta opción nos solicita el nombre del alumno que deseamos eliminar.
  • Modificar los datos de un alumno. Esta opción nos solicita primero el nombre de alumno para poder identificarlo y luego su nuevo nombre y edad.
  • Mostrar todos los alumnos almacenados. Esta opción recorre el ArrayList de alumnos e imprime los nombres de cada uno.

La clase servicio tendrá los métodos nuevoAlumno, eliminarAlumno, modificarAlumno y getAlumnos que realizarán las 4 operaciones antes citadas. Haremos las operaciones contra una base de datos utilizando Hibernate.

La base de datos tendrá 4 campos: id(PRIMARY KEY, AUTOINCREMENT), nombre (VARCHAR), apellidos (VARCHAR), fecha_alta(DATE).

icono de mandar un mail¡Contacta conmigo!
contacta conmigoPablo Monteserín

¡Hola! ¿En qué puedo ayudarte?