unidades:05_hibernate_query_language:05_optimizacion
Diferencias
Muestra las diferencias entre dos versiones de la página.
— | unidades:05_hibernate_query_language:05_optimizacion [2023/04/07 21:26] (actual) – creado - editor externo 127.0.0.1 | ||
---|---|---|---|
Línea 1: | Línea 1: | ||
+ | ====== Optimización ====== | ||
+ | Uno de los mayores problemas que nos podemos encontrar al usar un ORM es la lentitud que puede tener respecto a una aplicación realizada directamente con JDBC. | ||
+ | Cuando realizamos una aplicación mediante JDBC podemos determinar el número de SQL que van a lanzarse y optimizar cada una de ellas. Sin embargo, desde Hibernate inicialmente no podemos hacerlo ya que es Hibernate el que se encarga de realizar todas las SQL por nosotros. El no controlar las SQL nos puede llevar a problemas de rendimiento en nuestra aplicación. | ||
+ | |||
+ | Por suerte para nosotros y gracias a la potencia de Hibernate podemos ajustar mucho **cuántas SQLs** y **qué SQLs** lanza Hibernate. El problema de ello es que deberemos conocer más profundamente cómo funciona Hibernate con el coste que ello lleva asociado. | ||
+ | |||
+ | ===== Modelo ===== | ||
+ | En los ejemplos que vamos a realizar van a usarse las siguientes clases Java y tablas, que sólo mostraremos en formato UML. No vamos a poner el código fuente ni los ficheros de hibernate de mapeo ya que aún no han sido explicadas en lecciones anteriores todas las características que usan. | ||
+ | |||
+ | ==== Modelo de Java ==== | ||
+ | El modelo de clases Java es el siguiente: | ||
+ | |||
+ | <uml> | ||
+ | class Profesor | ||
+ | Profesor : int id | ||
+ | Profesor : String nombre | ||
+ | Profesor : String ape1 | ||
+ | Profesor : String ape2 | ||
+ | |||
+ | |||
+ | class CorreoElectronico | ||
+ | CorreoElectronico: | ||
+ | CorreoElectronico: | ||
+ | |||
+ | |||
+ | Profesor " | ||
+ | </ | ||
+ | |||
+ | ==== Modelo de Tablas ==== | ||
+ | El modelo de tablas es el siguiente: | ||
+ | |||
+ | <uml> | ||
+ | class Profesor << | ||
+ | Profesor : INTEGER id | ||
+ | Profesor : VARCHAR nombre | ||
+ | Profesor : VARCHAR ape1 | ||
+ | Profesor : VARCHAR ape2 | ||
+ | |||
+ | |||
+ | class CorreoElectronico << | ||
+ | CorreoElectronico: | ||
+ | CorreoElectronico: | ||
+ | CorreoElectronico: | ||
+ | |||
+ | |||
+ | Profesor " | ||
+ | </ | ||
+ | |||
+ | ===== ===== | ||
+ | |||
+ | |||
+ | Veamos ahora dos optimizaciones que podemos realizar en hibernate: | ||
+ | * [[#El problemas de las " | ||
+ | * [[# | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ===== El problemas de las " | ||
+ | Uno de los mayores problemas de los ORM es el problema de los " | ||
+ | |||
+ | <note warning> | ||
+ | |||
+ | Veamos ahora un ejemplo con Hibernate de este problema. | ||
+ | |||
+ | <code java 1> | ||
+ | Query query = session.createQuery(" | ||
+ | List< | ||
+ | for (Profesor profesor : profesores) { | ||
+ | System.out.println(profesor.toString()); | ||
+ | for (CorreoElectronico correoElectronico : profesor.getCorreosElectronicos()) { | ||
+ | System.out.println(" | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Este código lo único que hace es mostrar todos los profesores y para cada profesor mostrar todas sus direcciones de correo. Ësto lo hemos realizado lanzado únicamente una consulta HQL contra Hibernate. | ||
+ | |||
+ | Al ejecutar el programa y comprobar las SELECTs de SQL que se han lanzado podemos ver coóo se lanza una primera consulta para obtener todos los profesores pero posteriormente se lanza una consulta adicional por cada profesor para obtener los correos electrónicos de cada profesor. Es decir que se ejecutan "'' | ||
+ | |||
+ | ==== Solucion con left join fetch ==== | ||
+ | La solución más sencilla es modificar la consulta de HQL para que cargue también todos los correos electrónicos. Para ello deberemos hacer un '' | ||
+ | |||
+ | La consulta HQL que realiza el '' | ||
+ | <code sql> | ||
+ | SELECT p FROM Profesor p LEFT JOIN FETCH p.correosElectronicos | ||
+ | </ | ||
+ | |||
+ | Como vemos el '' | ||
+ | |||
+ | El código Java en este caso queda de la siguiente forma: | ||
+ | <code java 1> | ||
+ | Query query = session.createQuery(" | ||
+ | List< | ||
+ | for (Profesor profesor : profesores) { | ||
+ | System.out.println(profesor.toString()); | ||
+ | for (CorreoElectronico correoElectronico : profesor.getCorreosElectronicos()) { | ||
+ | System.out.println(" | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Vemos que sólo hemos modificado la línea 1 con la nueva consulta. | ||
+ | |||
+ | Si ejecutamos ahora el código podremos ver en la consola que se ha lanzado **unicamente** la siguiente '' | ||
+ | <code sql> | ||
+ | select profesor0_.Id as Id0_0_, correosele1_.IdCorreo as IdCorreo1_1_, | ||
+ | </ | ||
+ | |||
+ | En la SQL podemos ver como se realiza un //Left Outer Join// entre la tabla '' | ||
+ | |||
+ | Desgraciadamente con ésto no es suficiente. Si ejecutamos el código veremos que se repiten los objetos '' | ||
+ | El lenguaje SQL no permite consultar jerárquicas , así que lo que hace es retornar todos los profesores con todos los correos , por lo que el mismo profesor está repetido tantas veces como correos. | ||
+ | Hibernate desgraciadamente no elimina esa duplicidad así que debemos explicitamente desde Java eliminar los duplicados. | ||
+ | |||
+ | Para eliminar los objetos duplicados usaremos el truco de pasarlos todos a un <javadoc jdk7> | ||
+ | |||
+ | <code java 1> | ||
+ | Query query = session.createQuery(" | ||
+ | List< | ||
+ | |||
+ | Set< | ||
+ | profesores.clear(); | ||
+ | profesores.addAll(profesoresSinDuplicar); | ||
+ | |||
+ | for (Profesor profesor : profesores) { | ||
+ | System.out.println(profesor.toString()); | ||
+ | for (CorreoElectronico correoElectronico : profesor.getCorreosElectronicos()) { | ||
+ | System.out.println(" | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Vemos que en la línea 4 se añaden los datos de la lista al <javadoc jdk7> | ||
+ | |||
+ | Por último queda explicar porqué usamos un <javadoc jdk7> | ||
+ | |||
+ | Mas información en: | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[http:// | ||
+ | ==== Solucion con Lazy Loading | ||
+ | Hasta ahora no hemos hablado del Lazy Loading ( o carga perezosa en castellano ). El Lazy Loading consiste en que cuando lanzamos una consulta HQL , Hibernate por defecto intenta cargar el mínimo de datos posible ya que quizás no los necesitemos todos y sea una perdida de recursos. | ||
+ | |||
+ | Debido a las relaciones que hay entre los distintos objetos el cargar un objeto Java podría implicar cargar cientos de objetos relacionados con él. Imaginemos el siguiente diagrama UML con relaciones entre clase Java. | ||
+ | |||
+ | <uml> | ||
+ | class Centro | ||
+ | class Aula | ||
+ | class Familia | ||
+ | class Ciclo | ||
+ | class Modulo | ||
+ | class Profesor | ||
+ | class Alumno | ||
+ | |||
+ | Centro " | ||
+ | Centro " | ||
+ | Familia " | ||
+ | Familia " | ||
+ | Ciclo " | ||
+ | Modulo " | ||
+ | Modulo " | ||
+ | Centro " | ||
+ | </ | ||
+ | |||
+ | Nos puede haber parecido erróneo en el ejemplo inicial que Hibernate al cargar un profesor no haya cargado también sus correos electrónicos, | ||
+ | |||
+ | * Si cargamos un objeto '' | ||
+ | * pero si hemos cargado algún objeto de '' | ||
+ | * pero si hemos cargado algún objeto de '' | ||
+ | * pero si hemos cargado algún objeto de '' | ||
+ | |||
+ | Es decir que con una simple consulta HQL como la siguiente: | ||
+ | <code sql> | ||
+ | SELECT c FROM Centro c | ||
+ | </ | ||
+ | Se cargaría toda la base de datos en memoria, cosa que también empeoraría el rendimiento de la aplicación al saturar la memoria del servidor de aplicaciones. | ||
+ | |||
+ | Así que Hibernate hace una carga perezosa ( Lazy Loading ) cargando los datos sólo a medida que los vamos necesitando, | ||
+ | |||
+ | Resumiendo, el problema del '' | ||
+ | |||
+ | ===== Consultas nativas ===== | ||
+ | Los creadores de Hibernate no han sido tan soberbios como para pensar que las consultas en SQL que lanza Hibernate son las mejores que se pueden hacer. Por ello hibernate permite que el usuario pueda lanzar directamente consultas SQL contra la base de datos y de esa forma hacerlo de la forma más optimizada. | ||
+ | |||
+ | ==== createSQLQuery ==== | ||
+ | La clase <javadoc h41> | ||
+ | |||
+ | La posterior llamada al método <javadoc h41> | ||
+ | |||
+ | El siguiente ejemplo muestra cómo acceder a la **tabla** '' | ||
+ | |||
+ | <code java 1> | ||
+ | Query query = session.createSQLQuery(" | ||
+ | List< | ||
+ | |||
+ | for (Object[] datos : listDatos) { | ||
+ | System.out.println(datos[0] + " | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | * En la línea 1 vemos cómo se llama al método <javadoc h41> | ||
+ | * El resto de las líneas son iguales a lanzar una consulta HQL. | ||
+ | |||
+ | <note important> | ||
+ | Hay que fijarse que al ser una SQL ya se hace referencia a las tablas y columnas de la base de datos en vez de a las clases y propiedades Java | ||
+ | </ | ||
+ | |||
+ | ==== SQL Personalizadas ==== | ||
+ | Hibernate también permite que nosotros especifiquemos las SQL que van a usarse cuando se realiza una inserción , actualización y borrado en caso de que las que usa Hibernate tuvieran algún problema. | ||
+ | |||
+ | El fichero de mapeo de Hibernate incluye los siguientes 3 nuevos tags para especificar las SQL | ||
+ | * ''< | ||
+ | * ''< | ||
+ | * ''< | ||
+ | |||
+ | El siguiente fichero '' | ||
+ | <code xml 1|Profesor.hbm.xml> | ||
+ | <?xml version=" | ||
+ | < | ||
+ | < | ||
+ | <class name=" | ||
+ | <id column=" | ||
+ | < | ||
+ | </id> | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | | ||
+ | <set name=" | ||
+ | <key> | ||
+ | <column name=" | ||
+ | </ | ||
+ | < | ||
+ | </ | ||
+ | | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | Vemos cómo en las líneas 19, 20 y 21 se han definido las 3 SQL. Nótese que al ser SQL Nativas se podría usar cualquier característica especifica que necesitemos de la base de datos que estemos usando. | ||
+ | |||
+ | Si ejecutamos el siguiente código Java podremos ver por la consola las 3 SQL que hemos definido: | ||
+ | <code java 1> | ||
+ | System.out.println(" | ||
+ | Profesor profesor; | ||
+ | |||
+ | session.beginTransaction(); | ||
+ | profesor=new Profesor(" | ||
+ | session.save(profesor); | ||
+ | session.getTransaction().commit(); | ||
+ | |||
+ | session.beginTransaction(); | ||
+ | profesor.setNombre(" | ||
+ | session.update(profesor); | ||
+ | session.getTransaction().commit(); | ||
+ | |||
+ | session.beginTransaction(); | ||
+ | session.delete(profesor); | ||
+ | session.getTransaction().commit(); | ||
+ | </ | ||
+ | |||
+ | Como vemos en el código, se inserta una nueva fila en la línea 6 , en la línea 11 se actualiza y finalmente en la línea 15 se borra.Al ejecutarlo se muestra por consola lo siguiente: | ||
+ | |||
+ | Hibernate: select max(Id) from Profesor | ||
+ | Hibernate: INSERT INTO Profesor (Nombre, | ||
+ | Hibernate: UPDATE Profesor SET Nombre=?, | ||
+ | Hibernate: DELETE FROM Profesor WHERE Id=? | ||
+ | |||
+ | <note tip> | ||
+ | El ejemplo que hemos puesto es muy naif pero es normal en modelos de tablas complejos tener que optimizar las SQLs mediante el uso de [[wp> | ||
+ | </ | ||
+ | ===== Otras optimizaciones ===== | ||
+ | El tema de las optimizaciones en Hibernate es muy amplio pero no nos extenderemos más en el tema (( realmente Hibernate es muy amplio y en cada tema siempre hay que limitar lo que se explica )). Sin embargo debido a la importancia del rendimiento dejo unos enlaces con más información sobre el tema: | ||
+ | |||
+ | * [[http:// | ||
+ | * [[http:// |