Antes de adentrarnos en las razones para utilizar JPA, permítame explicarle rápidamente qué es. La API de persistencia de Java (JPA) es una especificación para la asignación relacional de objetos en Java. En cuanto a la mayoría de los estándares dentro del Proceso de la Comunidad Java, se implementa mediante diferentes marcos. El más popular es Hibernate.
Todas las implementaciones de JPA son compatibles con las características definidas por la especificación y, a menudo, las extienden con una funcionalidad personalizada. Esto proporciona 2 ventajas principales:
- Puede cambiar rápidamente su implementación de JPA, siempre y cuando no utilice ninguna característica propietaria.
- Las diferentes implementaciones pueden agregar características adicionales para innovar más rápido que el estándar. Algunos de ellos podrían convertirse en parte de la especificación en un momento posterior.
OK, suficiente teoría. Comencemos con una breve introducción a JPA y luego veamos algunas razones para usarlo.
Empezando con JPA
- ¿Cuáles son algunas herramientas indispensables para un maestro de kindergarten?
- ¿Cuáles son los mejores recursos para aprender Oracle Database?
- ¿Cuáles son algunas buenas fuentes (libros, tutoriales en video o sitios web) para aprender análisis de texto?
- ¿Cuál es el mejor recurso de aprendizaje (sitios de noticias, blogs, podcasts, etc.) para emprendedores?
- ¿Cuáles son los mejores recursos para aprender sobre la ciberseguridad?
Por supuesto, es imposible explicar JPA en toda su profundidad en una sola sección corta. Pero quiero mostrarle un caso de uso básico para que se familiarice con los conceptos generales.
Permite comenzar con el archivo persistence.xml. Su estructura está definida por el estándar JPA y proporciona la configuración al proveedor de persistencia, en primer lugar el controlador de la base de datos y la información de conexión. Puede ver una configuración de ejemplo simple en el siguiente fragmento de código.
Mi Unidad de Persistencia
org.hibernate.jpa.HibernatePersistenceProvider
false
Una vez que haya configurado su proveedor de persistencia, puede definir su primera entidad. El siguiente fragmento de código muestra un ejemplo de una asignación de entidad simple.
@Entidad
clase pública autor {
@Carné de identidad
@GeneratedValue (estrategia = GenerationType.AUTO)
@Column (nombre = “id”, actualizable = falso, nullable = falso)
Identificación larga privada;
@Versión
@ Columna (nombre = “versión”)
versión int privada;
@Columna
Nombre de la cadena privada;
@Columna
Cadena privada apellido;
@ManyToMany (mappedBy = “autores”)
set privado books = new HashSet ();
// constructores, getters / setters,
// y todo lo demás es como siempre
}
La anotación @Entity
define la clase Author
como una entidad. Se asigna a una tabla con el mismo nombre, en este caso la tabla de author
.
El atributo id
es la clave principal de la entidad y la tabla de la base de datos. La implementación de JPA genera automáticamente el valor de la clave principal y utiliza el atributo de versión para un bloqueo optimista para evitar actualizaciones simultáneas del mismo registro de la base de datos.
La anotación @Column
especifica que este atributo se asigna a una columna de base de datos. Similar a la anotación @Entity
, usa el nombre del atributo como el nombre de columna predeterminado.
La anotación @ManyToMany
define una relación con otra entidad. En este ejemplo, define la relación con la entidad Book
que se asigna a otra tabla de base de datos.
Como puede ver, solo necesita agregar algunas anotaciones para asignar una tabla de base de datos y usar otras funciones como el bloqueo optimista y la generación de clave principal.
5 razones
1. Productividad del desarrollador
La productividad del desarrollador es probablemente la ventaja más frecuentemente mencionada de JPA y cualquiera de sus implementaciones. El motivo principal es que debe definir la asignación entre las tablas de la base de datos y el modelo de su dominio una sola vez para usarla en todas las operaciones de escritura y la mayoría de sus operaciones de lectura. Además de eso, obtienes muchas funciones adicionales que de otro modo necesitarías implementar, como la generación de clave principal, la administración de concurrencia y diferentes optimizaciones de rendimiento.
Pero esa es solo una de las razones por las que JPA es popular por la productividad de sus desarrolladores. También proporciona una API simple pero muy eficiente para implementar operaciones básicas de CRUD. Puedes ver un ejemplo de eso en los siguientes 2 fragmentos de código.
En la primera, te muestro cómo persistir una nueva entidad de Author
en la base de datos.
EntityManager em = emf.createEntityManager ();
em.getTransaction (). begin ();
Autor a = nuevo Autor ();
a.setFirstName (“John”);
a.setLastName (“Doe”);
em.persist (a);
em.getTransaction (). commit ();
em.close ();
Como puedes ver, no hay mucho que tengas que hacer. Las primeras y últimas 2 líneas en este ejemplo son el código de plantilla, que necesita ejecutar solo una vez por cada transacción para obtener un EntityManager
y manejar la transacción. Si está utilizando JPA dentro de un contenedor Java EE o una aplicación Spring, puede ignorar estas líneas porque su marco se encarga de ello.
El trabajo principal se realiza en las líneas 4-7. Creo un nuevo objeto de la entidad Author
y llamo a los métodos de establecimiento para proporcionar el nombre y el apellido del nuevo autor. Luego llamo al método de persistencia en la interfaz de EntityManager
, que le indica a la implementación de JPA que genere una instrucción INSERT
SQL y la envíe a la base de datos.
El código del siguiente ejemplo es similar. Esta vez, quiero actualizar un autor existente. Como en el ejemplo anterior, las primeras y últimas 2 líneas del fragmento de código son código repetitivo para obtener un EntityManager y manejar la transacción. La parte interesante de estos fragmentos son las líneas 4 y 5. En la línea 4, uso el método de EntityManager
del EntityManager
para obtener una entidad por su clave principal. Como puede ver, no necesito escribir ningún SQL para esta consulta simple. Y es lo mismo para la actualización del apellido. Solo necesita llamar a los métodos de establecimiento de los atributos que desea cambiar y la implementación de JPA crea la instrucción SQL UPDATE
requerida para ello.
EntityManager em = emf.createEntityManager ();
em.getTransaction (). begin ();
Autor a = em.find (Author.class, 1L);
a.setLastName (“nuevo apellido”);
em.getTransaction (). commit ();
em.close ();
Como ha visto, JPA proporciona una API fácil de usar para implementar casos de uso de CRUD comunes sin escribir ningún SQL. Eso hace que la implementación de los casos de uso común sea mucho más rápida, pero también proporciona otro beneficio: sus sentencias de SQL no se extienden por todo el código. Eso significa que puede cambiar fácilmente el nombre de las tablas o columnas de la base de datos. Las únicas cosas que necesita adaptar son las anotaciones en su entidad.
2. Base de datos independiente
Si intenta utilizar el mismo código con diferentes bases de datos, se encontrará rápidamente con problemas causados por diferentes dialectos de SQL. SQL es el lenguaje estándar para interactuar con una base de datos, pero cada base de datos utiliza un dialecto ligeramente diferente. Esto se convierte en un gran problema si sus declaraciones tienen que ejecutarse en diferentes bases de datos.
Pero no si estás utilizando JPA. Proporciona una abstracción independiente de la base de datos sobre SQL. Mientras no esté utilizando ninguna consulta nativa, no tiene que preocuparse por la portabilidad de la base de datos. La implementación de JPA adapta las sentencias de SQL generadas en cada llamada a la API o consulta de JPQL al dialecto de la base de datos específica y maneja los diferentes tipos de datos específicos de la base de datos.
3. Manejo de tipos y parámetros
Debido a que los tipos de datos JDBC y Java no se alinean perfectamente, tendría que encontrar las combinaciones correctas y asegurarse de proporcionarlas como parámetros de consulta.
Si nunca lo ha hecho usted mismo, puede sonar fácil. Pero si tuvo que hacerlo al menos una vez, sabe que es fácil hacerlo mal. Peor aún, distrae la implementación de la lógica empresarial y también es la causa de las vulnerabilidades de inyección de SQL, uno de los problemas de seguridad más comunes en las aplicaciones web.
La mejor manera de evitar estos problemas y poder centrarse en la lógica de negocios es usar un marco o especificación, como JPA, que maneje estas cosas automáticamente.
Como ha visto al principio de esta publicación, no tiene que definir ningún tipo de datos SQL cuando define su asignación de entidad. La implementación de JPA oculta estas transformaciones de su código y utiliza una asignación predeterminada.
El manejo de parámetros para sus consultas JPQL toma un enfoque similar. Simplemente establece el parámetro en la interfaz de consulta y la implementación de JPA lo maneja en función de los metadatos de la entidad. Puedes ver un ejemplo de ello en el siguiente fragmento de código.
EntityManager em = emf.createEntityManager ();
em.getTransaction (). begin ();
TypedQuery q = em.createQuery (“SELECT a FROM Author a WHERE id =: id”, Author.class);
q.setParameter (“id”, 1L);
Autor a = q.getSingleResult ();
em.getTransaction (). commit ();
em.close ();
4. Evite consultas innecesarias
La optimización de escritura diferida es una de las muchas optimizaciones de rendimiento que obtiene con JPA. La idea básica es retrasar todas las operaciones de escritura el mayor tiempo posible para que se puedan combinar varias instrucciones de actualización en una. Su implementación JPA, por lo tanto, almacena todas las entidades que se usaron dentro de una transacción en el caché de primer nivel.
Debido a esto, el siguiente fragmento de código requiere solo una instrucción SQL UPDATE
, aunque la entidad se modifique en diferentes métodos dentro de la aplicación. Esto reduce la cantidad de sentencias SQL de forma masiva, especialmente en aplicaciones complejas y modularizadas.
public void updateAuthor () {
EntityManager em = emf.createEntityManager ();
em.getTransaction (). begin ();
Autor a = em.find (Author.class, 1L);
a.setFirstName (“Nuevo nombre”);
// Ejecutar operaciones que deben ocurrir.
// durante la transacción y entre
// actualizando nombre y apellido
a.setLastName (“nuevo apellido”);
em.getTransaction (). commit ();
em.close ();
}
5. almacenamiento en caché
El almacenamiento en caché es otra función de ajuste de rendimiento que obtiene casi gratis si usa JPA. Ya expliqué cómo se utiliza el caché de 1er nivel para la optimización de escritura diferida. Pero esa no es la única caché ni la única forma de beneficiarse de ella. JPA define 2 tipos diferentes de cachés:
- El caché de primer nivel, que contiene todas las entidades utilizadas dentro de una transacción.
- El caché de segundo nivel, que almacena las entidades de forma independiente de la sesión.
Ambos cachés le ayudan a reducir el número de sentencias de SQL ejecutadas al almacenar entidades en la memoria local. Esto puede proporcionar enormes mejoras de rendimiento si tiene que leer la misma entidad varias veces en la misma transacción o en varias transacciones. Lo mejor es que no necesita hacer casi nada para obtener estos beneficios.
El caché de primer nivel siempre está activado y no tiene que hacer nada para usarlo. Su implementación JPA lo utiliza internamente para mejorar el rendimiento de su aplicación.
El caché de segundo nivel debe activarse y puede hacerlo para todas o solo para entidades específicas. Tan pronto como haya activado el caché, su implementación de JPA lo utilizará de forma transparente. Por lo tanto, no necesita considerar el almacenamiento en caché mientras implementa la lógica de su negocio y puede activarlo o desactivarlo en cualquier momento sin ninguna refactorización. Siempre recomiendo activar el caché de segundo nivel para las entidades que lees muy a menudo sin cambiarlas. El almacenamiento en caché de estas entidades proporciona la mayoría de los beneficios de rendimiento y requiere solo una pequeña sobrecarga de administración para el caché.
La activación de la memoria caché de segundo nivel requiere dos pasos simples:
- Configure el caché en su archivo
persistence.xml
. - Marque una entidad como cacheable.
Veamos primero el archivo persistence.xml
. Lo único que debe hacer para configurar el caché de segundo nivel es configurar el parámetro de modo de caché compartido. En este ejemplo, uso el modo ENABLE_SELECTIVE
, que me permite habilitar el almacenamiento en caché para entidades específicas.
…
ENABLE_SELECTIVE
…
En el siguiente fragmento de código, agrego la anotación @Cacheable
a la entidad Author
para activar la caché de segundo nivel para ella:
@Entidad
@Cacheable
clase pública autor {
…
}
Eso es todo lo que necesita hacer para activar el caché de segundo nivel para una entidad dada y evitar consultas de base de datos innecesarias. Como ha visto, una configuración básica en JPA requiere solo un parámetro de configuración y una anotación. Pero el caché en sí no está definido por la especificación JPA, y es posible que deba proporcionar más parámetros de configuración para él.
Resumen
En esta publicación, presenté solo un pequeño subconjunto de las características y los beneficios proporcionados por JPA. Pero como ha visto, estas características cubren una amplia gama de temas, como la productividad del desarrollador, la portabilidad de la base de datos y las optimizaciones de rendimiento. JPA e Hibernate como su implementación más popular son, por lo tanto, la opción más común para implementar el acceso a la base de datos.
Sugerir
☞ Proyectos en MongoDB – Aprender MongoDB Construyendo diez proyectos
☞ Aprenda MongoDB: Liderando la base de datos NoSQL desde cero
☞ MongoDB 3.2 Fundamentos para Desarrolladores-Aprender por Ejercicios
☞ Aplicaciones Java: Creación de aplicaciones con Java
☞ Nuevas características de Java SE 8
☞ Programación de Java para principiantes – El último tutorial de Java