Imagine tener una herramienta que pueda detectar automáticamente JPA y problemas de rendimiento de Hibernación. Optimizador de hipersistencia es esa herramienta!
Introducción
Si se pregunta por qué y cuándo debe usar JPA o Hibernar, este artículo le proporcionará una respuesta a esta pregunta muy común. Debido a que he visto esta pregunta muy a menudo en el canal /r/java Reddit, decidí que vale la pena escribir una respuesta en profundidad sobre las fortalezas y debilidades de JPA e Hibernación.
Aunque JPA ha sido un estándar desde que se lanzó por primera vez en 2006, no es la única forma de implementar una capa de acceso a datos usando Java. Vamos a discutir las ventajas y desventajas de usar JPA o cualquier otra alternativa popular.
Por qué y cuándo se creó JDBC
En 1997, Java 1.1 introdujo la API JDBC (Java Database Connectivity), que fue muy revolucionaria para su época, ya que ofrecía la posibilidad de escribir la capa de acceso a datos una vez utilizando un conjunto de interfaces y ejecutarla en cualquier base de datos relacional que implementara la API JDBC sin necesidad de cambiar el código de su aplicación.
La API JDBC ofrecía una interfaz Connection
para controlar los límites de las transacciones y crear instrucciones SQL simples a través de la API Statement
o instrucciones preparadas que le permiten vincular valores de parámetros a través de la API PreparedStatement
.
Entonces, asumiendo que tenemos una tabla de base de datos post
y queremos insertar 100 filas, así es como podríamos lograr este objetivo con JDBC:
int postCount = 100;int batchSize = 50;try (PreparedStatement postStatement = connection.prepareStatement(""" INSERT INTO post ( id, title ) VALUES ( ?, ? ) """)) { for (int i = 1; i <= postCount; i++) { if (i % batchSize == 0) { postStatement.executeBatch(); } int index = 0; postStatement.setLong( ++index, i ); postStatement.setString( ++index, String.format( "High-Performance Java Persistence, review no. %1$d", i ) ); postStatement.addBatch(); } postStatement.executeBatch();} catch (SQLException e) { fail(e.getMessage());}
Si bien aprovechamos los bloques de texto de varias líneas y los bloques de prueba con recursos para eliminar la llamada PreparedStatement
close
, la implementación sigue siendo muy detallada. Tenga en cuenta que los parámetros de enlace comienzan desde 1
, no desde 0
, como podría estar acostumbrado desde otras API conocidas.
Para obtener las primeras 10 filas, podríamos necesitar ejecutar una consulta SQL a través de PreparedStatement
, que devolverá un ResultSet
que representa el resultado de la consulta basada en tablas. Sin embargo, dado que las aplicaciones utilizan estructuras jerárquicas, como JSON o DTOs para representar asociaciones padre-hijo, la mayoría de las aplicaciones necesitaban transformar el JDBC ResultSet
a un formato diferente en la capa de acceso a datos, como se ilustra en el siguiente ejemplo:
int maxResults = 10;List<Post> posts = new ArrayList<>();try (PreparedStatement preparedStatement = connection.prepareStatement(""" SELECT p.id AS id, p.title AS title FROM post p ORDER BY p.id LIMIT ? """)) { preparedStatement.setInt(1, maxResults); try (ResultSet resultSet = preparedStatement.executeQuery()) { while (resultSet.next()) { int index = 0; posts.add( new Post() .setId(resultSet.getLong(++index)) .setTitle(resultSet.getString(++index)) ); } }} catch (SQLException e) { fail(e.getMessage());}
De nuevo, esta es la mejor manera de escribir esto con JDBC, ya que estamos usando Bloques de texto, probar con recursos y una API de estilo fluido para construir los objetos Post
.
Sin embargo, la API JDBC sigue siendo muy detallada y, lo que es más importante, carece de muchas características que se requieren al implementar una capa de acceso a datos moderna, como:
- Una forma de obtener objetos directamente del conjunto de resultados de la consulta. Como hemos visto en el ejemplo anterior, necesitamos iterar
ReusltSet
y extraer los valores de columna para establecer las propiedades del objetoPost
. - Una forma transparente de obtener instrucciones por lotes sin tener que reescribir el código de acceso a datos al cambiar del modo predeterminado sin lotes a usar procesamiento por lotes.
- soporte para bloqueo optimista
- Una API de paginación que oculta la sintaxis de consulta Top-N y Next-N específica de la base de datos
Por qué y cuándo se creó Hibernate
En 1999, Sun lanzó J2EE (Java Enterprise Edition), que ofrecía una alternativa a JDBC, llamada Entity Beans.
Sin embargo, dado que los Frijoles de Entidad eran notoriamente lentos, complicados y engorrosos de usar, en 2001, Gavin King decidió crear un marco de trabajo OR que pudiera asignar tablas de bases de datos a POJOs (Objetos Java antiguos simples), y así nació Hibernate.
Siendo más ligero que Entity Beans y menos detallado que JDBC, Hibernate se hizo más y más popular, y pronto se convirtió en el framework de persistencia de Java más popular, ganando a JDO, iBATIS, Oracle TopLink y Apache Cayenne.
¿Por qué y cuándo se creó JPA?
Aprendiendo del éxito del proyecto Hibernate, la plataforma Java EE decidió estandarizar la forma en que Hibernate y Oracle TopLink, y así nació JPA (Java Persistence API).
JPA es solo una especificación y no se puede usar por sí sola, proporcionando solo un conjunto de interfaces que definen la API de persistencia estándar, que es implementada por un proveedor de JPA, como Hibernate, EclipseLink o OpenJPA.
Al usar JPA, debe definir la asignación entre una tabla de base de datos y su objeto de entidad Java asociado:
@Entity@Table(name = "post")public class Post { @Id private Long id; private String title; public Long getId() { return id; } public Post setId(Long id) { this.id = id; return this; } public String getTitle() { return title; } public Post setTitle(String title) { this.title = title; return this; }}
Después, podemos reescribir el ejemplo anterior que guardó 100 registros post
se ve así:
for (long i = 1; i <= postCount; i++) { entityManager.persist( new Post() .setId(i) .setTitle( String.format( "High-Performance Java Persistence, review no. %1$d", i ) ) );}
Para habilitar inserciones por lotes JDBC, solo tenemos que proporcionar una sola propiedad de configuración:
<property name="hibernate.jdbc.batch_size" value="50"/>
Una vez que se proporciona esta propiedad, Hibernate puede cambiar automáticamente de sin lotes a lotes sin necesidad de cambiar el código de acceso a datos.
Y, para obtener las primeras 10 post
filas, podemos ejecutar la siguiente consulta JPQL:
int maxResults = 10;List<Post> posts = entityManager.createQuery(""" select p from post p order by p.id """, Post.class).setMaxResults(maxResults).getResultList();
Si compara esto con la versión JDBC, verá que JPA es mucho más fácil de usar.
Las ventajas y desventajas de usar JPA e Hibernar
JPA, en general, e Hibernar, en particular, ofrecen muchas ventajas.
- Puede obtener entidades o DTO. Incluso puede obtener la proyección DTO jerárquica padre-hijo.
- Puede habilitar el procesamiento por lotes JDBC sin cambiar el código de acceso a los datos.
- tiene soporte para bloqueo optimista.
- Tiene una abstracción de bloqueo pesimista que es independiente de la sintaxis subyacente específica de la base de datos para que pueda adquirir un BLOQUEO de LECTURA y ESCRITURA o incluso un BLOQUEO DE OMISIÓN.
- Tiene una API de paginación independiente de la base de datos.
- Puede proporcionar un
List
de valores a una cláusula IN query, como se explica en este artículo. - Puede utilizar una solución de almacenamiento en caché muy consistente que le permite descargar el nodo primario, al que, para transacciones de escritura rea, solo se puede llamar verticalmente.
- Tiene soporte incorporado para el registro de auditoría a través de Envers Hibernados.
- Tiene soporte incorporado para multitenencia.
- Puede generar un script de esquema inicial a partir de las asignaciones de entidades con la herramienta Hibernate hbm2ddl, que puede suministrar a una herramienta de migración automática de esquemas, como Flyway.
- No solo tiene la libertad de ejecutar cualquier consulta SQL nativa, sino que también puede usar SqlResultSetMapping para transformar el JDBC
ResultSet
en entidades JPA o DTO.
Las desventajas de usar JPA e Hibernar son las siguientes:
- Si bien comenzar con JPA es muy fácil, convertirse en un experto requiere una inversión de tiempo significativa porque, además de leer su manual, todavía tiene que aprender cómo funcionan los sistemas de bases de datos, el estándar SQL y el tipo SQL específico utilizado por su base de datos de relaciones de proyecto.
- Hay algunos comportamientos menos intuitivos que pueden sorprender a los principiantes, como el orden de operación de descarga.
- La API de Criterios es bastante detallada, por lo que debe usar una herramienta como Codota para escribir consultas dinámicas más fácilmente.
La comunidad general y las integraciones populares
JPA e Hibernate son extremadamente populares. Según el informe del ecosistema Java de 2018 de Snyk, Hibernate es utilizado por el 54% de todos los desarrolladores Java que interactúan con una base de datos relacional.
Este resultado puede ser respaldado por Google Trends. Por ejemplo, si comparamos las tendencias de Google de JPA con sus principales competidores (por ejemplo, MyBatis, QueryDSL y jOOQ), podemos ver que JPA es muchas veces más popular y no muestra signos de perder su posición dominante en el mercado.
Ser tan popular trae muchos beneficios, como:
- La integración de Spring Data JPA funciona a la perfección. De hecho, una de las razones más importantes por las que JPA e Hibernación son tan populares es porque Spring Boot utiliza Spring Data JPA, que, a su vez, utiliza Hibernación entre bastidores.
- Si tiene algún problema, es muy probable que estas respuestas de StackOverflow relacionadas con la hibernación de 30k y las respuestas de StackOverflow relacionadas con JPA de 16k le proporcionen una solución.
- Hay tutoriales de hibernación de 73k disponibles. Solo mi sitio ofrece más de 250 tutoriales de JPA e Hibernación que te enseñan cómo aprovechar al máximo JPA e Hibernación.
- También puede utilizar muchos cursos de vídeo, como mi curso de vídeo de persistencia de Java de alto rendimiento.
- Hay más de 300 libros sobre Hibernación en Amazon, uno de los cuales también es mi libro de persistencia de Java de alto rendimiento.
Alternativas JPA
Una de las mejores cosas del ecosistema Java es la abundancia de marcos de trabajo de alta calidad. Si JPA e Hibernación no se ajustan bien a su caso de uso, puede usar cualquiera de los siguientes marcos:
- MyBatis, que es un framework de mapeador de consultas SQL muy ligero.
- QueryDSL, que le permite crear consultas SQL, JPA, Lucene y MongoDB de forma dinámica.
- jOOQ, que proporciona un metamodelo Java para las tablas subyacentes, los procedimientos almacenados y las funciones, y le permite crear una consulta SQL de forma dinámica utilizando un DSL muy intuitivo y de forma segura.
Por lo tanto, use lo que funcione mejor para usted.
Talleres en línea
Si disfrutaste de este artículo, apuesto a que te encantará mi próximo Taller en Línea de Persistencia de Java de 4 días x 4 horas de Alto Rendimiento
Conclusión
En este artículo, vimos por qué se creó JPA y cuándo debería usarlo. Si bien JPA trae muchas ventajas, tiene muchas otras alternativas de alta calidad para usar si JPA e Hibernación no funcionan mejor para sus requisitos actuales de aplicación.
Y, a veces, como expliqué en esta muestra gratuita de mi libro de persistencia de Java de alto rendimiento, ni siquiera tiene que elegir entre JPA u otros frameworks. Puedes combinar fácilmente JPA con un framework como jOOQ para obtener lo mejor de ambos mundos.