{"id":705,"date":"2019-05-28T09:48:54","date_gmt":"2019-05-28T14:18:54","guid":{"rendered":"http:\/\/gregorgonzalez.com.ve\/blog\/?p=705"},"modified":"2019-05-28T09:58:32","modified_gmt":"2019-05-28T14:28:32","slug":"algunos-consejos-sobre-hibernate","status":"publish","type":"post","link":"https:\/\/gregorgonzalez.com.ve\/blog\/algunos-consejos-sobre-hibernate\/","title":{"rendered":"Algunos consejos sobre Hibernate"},"content":{"rendered":"<p>En un par de a\u00f1os he trabajado con varios proyectos que utilizan Hibernate y siempre veo los mismos errores, o mejor dicho, costumbres que pueden perjudicar el rendimiento de una aplicaci\u00f3n. Todos estamos propensos a cometer errores y m\u00e1s cuando est\u00e1s empezando, sin embargo con el tiempo, la experiencia y la investigaci\u00f3n activa, podemos intuir sobre que podemos mejorar y evitar estos contratiempos.<\/p>\n<p>Por ello escribo este post, tanto para aconsejar como para recordarme a mi mismo sobre los peque\u00f1os detalles que necesitamos tomar en cuenta a la hora de crear nuestra aplicaci\u00f3n. Son cosas simples que a la larga nos puede resultar problem\u00e1tico en aplicaciones grandes o cuando manejamos grandes registros.<\/p>\n<h3>Recuperar registros con el modo Eager\/Lazy<\/h3>\n<p>No saben la cantidad de veces que he visto como se perjudica el rendimiento en los grandes listados que muestra una aplicaci\u00f3n debido al uso o desconocimiento del modo Eager de Hibernate. Cuando mapeamos las relaciones en los Entity, nos permite indicar que tipo de recuperaci\u00f3n podemos utilizar.<\/p>\n<p>Hibernate nos provee de Lazy y Eager, el Eager indica que traiga todos los registros asociados en la relaci\u00f3n, mientras que Lazy no, solo lo traer\u00e1 al momento de utilizar el objeto relacionado.<\/p>\n<p>Esto es importante al momento de realizar una secci\u00f3n de la aplicaci\u00f3n, por ejemplo, si tenemos una tabla de <b>Autores<\/b> y otro de <b>Libros<\/b>, si solo vamos a mostrar un gran listado de autores con las columnas principales del autor y no necesitamos detalle del libro, es ideal utilizar Lazy y evitar esa consulta adicional. Si en cambio debemos mostrar tambi\u00e9n detalles de cada libro en el mismo listado se puede dejar el Eager. Lo mismo se tomar\u00eda en cuenta si queremos mostrar un listado de libros.<\/p>\n<p>Ejemplo usando Lazy:<\/p>\n<pre class=\"lang:java decode:true \">@Entity\r\npublic class Autor{ \r\n    @ManyToMany(mappedBy=\"autores\", fetch=FetchType.LAZY)\r\n    private List&lt;Libro&gt; libros = new ArrayList&lt;Libro&gt;();\t\r\n    \/* M\u00e1s C\u00f3digo *\/\r\n}<\/pre>\n<h3>Tomar en cuenta el valor por defecto<\/h3>\n<p>Cuando no se indica el tipo de recuperaci\u00f3n de registros, Hibernate utiliza Eager para relaciones \u00abToOne\u00bb y al contrario utiliza Lazy, es una buena pr\u00e1ctica que nosotros mismos establezcamos el tipo de recuperaci\u00f3n y sea f\u00e1cilmente reconocible.<\/p>\n<p>Ejemplo con ToOne cambiando a Lazy:<\/p>\n<pre class=\"lang:java decode:true \">@Entity\r\npublic class Autor{ \r\n    @ManyToMany(mappedBy=\"autores\", fetch=FetchType.LAZY)\r\n    private List&lt;Libro&gt; libros = new ArrayList&lt;Libro&gt;();\t\r\n    \/* M\u00e1s C\u00f3digo *\/\r\n}<\/pre>\n<h3>Usar query directos y Lazy en subconsultas<\/h3>\n<p>Cuando trabajamos en grandes reportes o necesitamos obtener los detalles de los registros para luego mostrarlos, se puede realizar simplemente llamando la relaci\u00f3n \u00abautor.getLibros()\u00bb y entonces Hibernate se encargar\u00e1 de realizar la subconsulta. Esto puede ser muy contraproducente en grandes listados o reportes, porque con cada subquery estamos sobrecargando de consultas a la base de datos.<\/p>\n<p>Ejemplo subconsulta:<\/p>\n<pre class=\"lang:java decode:true\">List&lt;Autor&gt; autores = em.createQuery(\"SELECT a FROM Autor a\", Autor.class).getResultList();\r\nfor (Autor a : autores) {\r\n    System.out.println(\"Autor: \" + a.getNombre() + \" \" + a.getApellido() + \" Total Libros: \" + a.getLibros().size());\r\n}<\/pre>\n<p>Al indicar getLibros, Hibernate realiza una subconsulta para obtener el detalle, en estos casos es com\u00fan utilizarlo, no es ning\u00fan problema este modo de trabajo pero para grandes reportes de cientos de registros, lo mejor es inicializar con la relaci\u00f3n en el query usado Fetch de la siguiente manera:<\/p>\n<pre class=\"lang:java decode:true \">List&lt;Autor&gt; autores = em.createQuery(\r\n    \"SELECT a FROM Autor a JOIN FETCH a.libros\",\r\n    Autor.class).getResultList();<\/pre>\n<p>Con ello nos trae de un vez los detalles en una sola consulta, aumentando enormemente el rendimiento.<\/p>\n<h3>Usar limite de registros<\/h3>\n<p>C\u00f3mo sabemos en ciertas consultas JPQL y en ciertas base de datos, no existe las palabras claves \u00abLimit\u00bb o incluso \u00abOffset\u00bb y es com\u00fan que nadie los tome en cuenta con aplicaciones en Hibernate, pero a medida que se incrementan los registros se generan problemas de rendimiento, lentitud, al traer todos los registros de una vez. Para ello o cuando queremos realizar paginaci\u00f3n, Hibernate nos provee de varios m\u00e9todos para realizar esto.<\/p>\n<p>Ejemplo usando limites y offset:<\/p>\n<pre class=\"lang:java decode:true \">List&lt;Autor&gt; autores = em.createQuery(\"SELECT a FROM Autor a ORDER BY a.nombre ASC\", Autor.class)\r\n    .setMaxResults(5)\r\n    .setFirstResult(0)\r\n    .getResultList();<\/pre>\n<h3>Usar Bind Parameters en los query<\/h3>\n<p>Al crear consultas manuales donde nosotros indicamos el query, muchas personas tienden a concatenar en un string \u00abWHERE id = \u00bb + id y esto es considerado una mala pr\u00e1ctica, Hibernate nos provee de par\u00e1metros enlazados, una forma mas sencilla y la cual nos trae varias ventajas, f\u00e1cil de usar, conversiones autom\u00e1ticas, escapa las variables para prevenir inyecciones SQL y permite un mejor rendimiento al optimizar los query.<\/p>\n<p>Ejemplo usando Bind Parameters:<\/p>\n<pre class=\"lang:java decode:true \">TypedQuery&lt;Autor&gt; consulta = em.createQuery(\"SELECT a FROM Autor a WHERE a.id = :id\", Author.class);\r\nconsulta.setParameter(\"id\", 1L);\r\nAutor a = consulta.getSingleResult();<\/pre>\n<h3>Usar la l\u00f3gica de negocio en la base de datos<\/h3>\n<p>En java puede ser tedioso realizar todos los procesos en la aplicaci\u00f3n y luego estar compilando, realizando deploy, verificando y cambiando al servidor desarrollo\/producci\u00f3n, y si luego desean realizar la misma operaci\u00f3n pero desde otro sistema o utilizar una aplicaci\u00f3n m\u00f3vil, se tendr\u00eda que volver a codificar la l\u00f3gica en cada app. Para evitar esto es costumbre pasar la mayor\u00eda de l\u00f3gica de negocio a la base de datos, creando nuestras propias consultas, vistas, funciones y procedimientos almacenados.<\/p>\n<p>Centralizamos en la base de datos y todas estos procesos pueden ser llamados f\u00e1cilmente desde Hibernate. JPA nos brinda varias funciones por defecto, como por ejemplo \u00absize\u00bb que totaliza lo contenido en un Collection.<\/p>\n<p>Ejemplo llamando funci\u00f3n simple:<\/p>\n<pre class=\"lang:java decode:true \">Query consulta = em.createQuery(\"SELECT a, size(a.libros) FROM Autor a GROUP BY a.id\");<\/pre>\n<p>Por ejemplo suponiendo que tenemos varias funciones propias o procedimientos almacenados.<\/p>\n<p>Ejemplo llamando funci\u00f3n con JPA function:<\/p>\n<pre class=\"lang:plsql decode:true \">CREATE OR REPLACE FUNCTION calculate(\r\n    IN a number,\r\n    IN b number,\r\n    OUT sum number)\r\nRETURNS number AS\r\nBEGIN\r\n    suma = a + b;\r\nEND;<\/pre>\n<pre class=\"lang:java decode:true \">TypedQuery&lt;Libro&gt; consulta = em.createQuery(\"SELECT lib FROM Libro lib WHERE lib.id = function('sumar', 1, 2)\", Libro.class);<\/pre>\n<p>Usar funciones propias en el WHERE se pueden realizar sin inconvenientes porque se detecta el tipo de valor de retorno. Sin embargo si es una funci\u00f3n que vas a usar en la parte de SELECT debes registrarla previamente en el dialect de Hibernate.<\/p>\n<p>En cuanto a los procedimientos almacenados, es similar al de las funciones y se registran en el entity con la anotaci\u00f3n NamedStoredProcedureQuery y se llamar\u00eda como un m\u00e9todo del objeto.<\/p>\n<p>Estos procesos son largos para explicarlos a detalle en este post, solo es para que tomen en cuenta que si se pueden utilizar.<\/p>\n<h3>Realizar update o delete masivos<\/h3>\n<p>Cuando debemos eliminar grandes registros o actulizarlos, mucha gente recurre a realizar un select de todos los registros y en un while eliminar uno por uno, esto no es problema con pocos registros, pero si se deben realizar en toda la tabla, se convierte en algo contraproducente y muy lento.<\/p>\n<p>Por ejemplo, queremos actualizar los precios de todos los libros un 20%:<\/p>\n<pre class=\"lang:java decode:true \">Query consulta = em.createQuery(\"UPDATE Libro lib SET lib.precio = lib.precio * 1.2\");\r\nconsulta.executeUpdate();<\/pre>\n<h3>Grandes y complejas consultas SQL<\/h3>\n<p>Aunque Hibernate se puede utilizar pr\u00e1cticamente en todo, hay que reconocer cuando no debe ser utilizado, evitar usar m\u00e9todos autom\u00e1ticos y realizar tu mismo las consultas. Por ejemplo, los Named Query, nos permite realizar consultas personalizas y obtener lo que necesitamos, aun as\u00ed tiene sus limitaciones y en ese caso podemos recurrir a Native Query.<\/p>\n<p>La idea de esto es evitar que al trabajar con todos los registros, luego al llamar un entity manager y usar sus funciones propias tipo \u00abfind\u00bb, se trae todo en conjunto a los relacionados, ocasionando lentitud y consultas adicionales para informaci\u00f3n que tal vez no requerimos, en especial en ciertos reportes. Al trabajar con tanta informaci\u00f3n, tantas tablas y entities, hay ocasiones que puede ocurrir loops infinitos y son cosas que hay que preveer. Incluso no se recomienda usar Hibernate si solo va a realizar puras consultas para mostrar informaci\u00f3n, solo selects, Hibernate es adecuado para la manipulaci\u00f3n de registros.<\/p>\n<h3>Trabajar con vistas<\/h3>\n<p>Para no realizar complejos SQL, o al trabajar con grandes reportes, lo mejor es hacerlo en base de datos, realizar toda la l\u00f3gica en una vista y que quede lista solo para mostrar los datos. Hibernate no solo permite trabajar con tablas sino con vistas tambi\u00e9n y si la base de datos tiene la opci\u00f3n, se puede modificar registros desde una vista.<\/p>\n<p>Algo que me encuentro en cada trabajo, las vistas pueden ser lentas dependiendo de como se hallan realizado. Son consultas muy \u00fatiles pero se debe verificar que tantos procesos llama, si provienen de otras vistas, si llama procedimientos almacenados, etc.<\/p>\n<p>Evita usar vistas de vistas que llaman a otras vistas, verifica si son simples o complejas y toma el tiempo de consulta para verificar que no da\u00f1e el rendimiento de la aplicaci\u00f3n. Evitar complejos subquery, donde la vista aparte de todos los datos de la tabla a mostrar, tambi\u00e9n ejecutar\u00e1 el subquery en cada uno de los registros, algunos se pueden cambiar por simples join y as\u00ed evitar m\u00faltiples consultas.<\/p>\n<p>Por ejemplo, me ocurr\u00eda con una vista que al buscar un detalle por id o rango de fechas era super r\u00e1pido pero luego al realizar la consulta m\u00e1s amplias, rangos de fechas m\u00e1s grandes o traer todos los registros, se pod\u00eda tardar horas! Todo esto se modific\u00f3 y se optimiz\u00f3 enormemente verificando los subquery uno a uno.<\/p>\n<p>Usa \u00abEXPLAIN\u00bb de la base de datos para verificar el proceso que realiza una consulta y determinar donde est\u00e1 tomando tanto tiempo y as\u00ed ver que se puede mejorar.<\/p>\n<p>&nbsp;<\/p>\n<p>Espero les sea de utilidad y no solo para estos casos de Java, tambi\u00e9n pueden aplicar para otros lenguajes y bases de datos.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>En un par de a\u00f1os he trabajado con varios proyectos que utilizan Hibernate y siempre veo los mismos errores, o mejor dicho, costumbres que pueden perjudicar el rendimiento de una aplicaci\u00f3n. Todos estamos propensos a cometer errores y m\u00e1s cuando est\u00e1s empezando, sin embargo con el tiempo, la experiencia y la investigaci\u00f3n activa, podemos intuir [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":688,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_exactmetrics_skip_tracking":false,"_exactmetrics_sitenote_active":false,"_exactmetrics_sitenote_note":"","_exactmetrics_sitenote_category":0,"footnotes":""},"categories":[227],"tags":[246,228,245,258,259,55],"_links":{"self":[{"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/posts\/705"}],"collection":[{"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/comments?post=705"}],"version-history":[{"count":5,"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/posts\/705\/revisions"}],"predecessor-version":[{"id":710,"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/posts\/705\/revisions\/710"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/media\/688"}],"wp:attachment":[{"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/media?parent=705"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/categories?post=705"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/tags?post=705"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}