JPA (Java Persistence API)

Persistence Context

Describe la relación entre las entidades de nuestra aplicación y su representación en la base de datos. En este contexto nos referimos a Instancia (Instance) a una copia especifica de una Entidad en memoria. Podemos tener varias instancias de la misma Entidad, con distintos valores y estados. Cada Entidad deberá tener su propio indentificador único, que es lo que permitirá a JPA identificar de manera única a cada instancia en la base de datos. Puede pasarnos que pongamos el mismo id a dos entidades, el Contexto de Persistencia gestiona esta situación mediante los Estados de Entidad (Persistence Context Entity States).

Persistence Context Entity States

Transient: sin asociación con el Contexto de Persistencia, a menudo no tiene ni un Id asignado. Managed: Persistente, manejado por el Contexto de Persistencia, así que cualquier cambio en la entidad se verá reflejado en la base de datos. Detached: Manejado anteriormente, lo que les pasa a todas las entidades cuando el Contexto de Persistencia finaliza. Removed: Agendado para ser eliminado de la base de datos, aunque el objeto Java seguirá existiendo y tendra un id.

Entity Manager

Para poder manegar que el estado de nuestras entidades en el Persistence Context es a traves de lo que se conocer como el Entity Manager. Que podemos injectar en nuestro código a traves de la anotación @PersistenceContext. Tenemos cuatro métodos que podemos utilizar:
  1. Persist: coge una entidad no Managed y la guarda en base de datos.
  2. Find: busca un id en base de datos y devuelve una entidad Managed
  3. Merge: actualiza una entidad que esta en modo Detached y devuelve una entidad ya Managed. Si la entidad no existe, persiste la Entidad creando una nueva fila.
  4. Remove: detaches y borra una entidad de la base de datos.
Ejemplo:

Lazy Loading

Esta es una estrategia que nos permite ahorrar accesos a base de datos o memoria basado en no cargar los datos asociados a una entidad cuando hasta que estos no sean necesarios. En este ejemplo decimos que no se carguen los outfits de la persona cuando hagamos acceso a la persona:
Por defecto, estás son las formas de carga de asociaciones:

FetchType.LAZY:
  • @OneToMany
  • @ManyToMany
FetchType.EAGER:
  • @ManyToOne
  • @OneToOne

Persistencia en casacada

Cuando persistimos una entidad, sus datos asociados no son salvados en la base de datos por defecto. Si queremos que esto ocurra tenemos que hacerlo manualmente o especificarlo a través del atributo cascade. Tipos de CascadeType: FIND, PERSIST, REMOVE, ALL.

Queries

JPA utiliza JPQL para escribir queries para el Entity Manager. La sintaxis es muy parecida a SQL, donde las tablas son las entidades y las columnas los atributos:
Ejemplo de query:
Ejemplo de query accediendo a los asociados:

Named Queries

JPA Named Queries nos permiten definir las queries a nivel de clase de dominio evitando las repeticiones. Lo que propone es agrupar las queries que se refieren a la misma entidad en un mismo lugar. Una posible solución es declarar constantes con las queries junto a los atributos de clase, incluso creando fragmentos de query.
Hibernate proporciona una herramienta para hacer esto, a través de la anotación @NamedQuery y @NamedQueries. Hibernate compila los NamedQueries al arrancar la aplicación, validando que las entidades que estas quering pueden ser creadas. Una vez creadas podemos usar el Entity Manager para ejecutarlas.

Un detalle importante a tener en cuenta es no repetir ningun nombre de query en otras entidades, ya que el scope de estas queries es el Persistence Unit total. Una convención típica es utilizar el nombre de la entidad como prefijo.
Otra opción que tenemos es usar es el JPA Criteria Interface, con el que construimos nuestras queries usando métodos Java. Este método genera más código pero nos ayuda a crear queries sintacticamente válidas.

Projections

Projections son una buena forma de combinar entidades no relacionadas o para devolver Value types (int, string, booleans, etc), y nos permiten devolver también non-Entity. Para todo lo demás DTO o JSONView son mejor opción.

Proyectando en un Value Type
Proyectando en un objeto non-Entity

Patrón de diseño Repository Pattern

Los Entities son nuestros objetos gestionados por el Entity Manager en el Contexto de Persistencia, en memoria. La manera que tenemos de pasar este conexto a base de datos es a traves de este patrón.

La anotación @Component marca una clase para el escaneado de componentes, de esta anotación existen especializaciones como @Controller, @Service y la que comentamos a continuación @Repository. Así que cualquier componente marcado como @Repository quedará listo para inyección de dependencias.

Spring convertirá las excepciones de la persistnecia en excepciones Spring. También lo usamos para identificar componentes que interactian con la base de datos.

Este patrón es una forma de pensar en la base de datos como una colección de objetos. La idea es crear un interface para nuestra clase Repository como sería un interfaz de ua clase Java cualquiera, como Map o Set. Ejemplo:
Gestión de Repositorios

Lo normal es tener un repositorio por cada entidad de nuestra aplicación. Pero a lo largo del tiempo podemos acabar con muchos repositorios e interfaces. Y ya que los repositorios suelen incluir las mismas operaciones de persistencia, vamos a cabar con mucha redundancia y dificultades para mantener. Estas son algunas de las opciones para solucionar este problema:

Una interfaz, una implementación: la interfaz solo contiene lo que necesites aunque la implementación incluya más métodos. La mayor ventaja es que controlas en nivel de acceso de la aplicación a esas entidades. Lo negativo es que acabas con mucha duplicidad ya que tendrás una interfaz y una implementación por cada interfaz sin compartir código, por

Otra opción es tener una interfaz genérica que todo los repositories tendrán que implementar. La ventaja es que puedes tener una interfaz con los métodos usados por todas tus entitites, y la parte negativa es que a lo mejor acabas implementando métodos que alguna entitie no necesita en absoluto.

La otra opción, y seguramente la mejor, es crear una clase repositorio abstracta que implementa todos los métodos de nuestra interfaz genérica. Esta clase podrá contener implementaciones genéricas de métodos asi no tendremos que implementarlas en las entities que no las necesiten.

Spring Data JPA

Si JPA define el API para mapear objetos a tablas de la base de datos, e Hibernate implementa el API del JPA. Spring Data JPA es una colección de código y herramientas que nos ayuda a usar JPA.

Para pdoer usar Spring Data JPA tenemos que extender de una de las interfaces de Spring Data. En este ejemplo tenemos acceso a las ooperaciones CRUD:
Y podemos extender las funcionalidades del repositorio añadiendo nuevos métodos a la interfaz:

Flushing y Transactions

Flushing es el proceso de sincronizar contenido en la capa de contexto con la base de datos. La capa de conexto funciona como Level 1 Cache, porque no escribe en la base de datos hasta el flushing. Y una transacción es un grupo de operaciones que se ejecutan con exito o fallan como grupo. Se utiliza la anotación @Transactional.

Lo que produce el Flushing es:
  • Finalización de una transacción.
  • Cuando se solicita información que esta en cola para ser modificada.
  • Cuando ejecutamos queries directamente sin pasar por el contexto de persistencia.
Ejemplo de una transacción:

Comentarios

Entradas populares de este blog

Envío de checkboxes o selector multiple por AJAX con jQuery

Patrones de Diseño Creacionales

Conceptos básicos de la POO