Acceso a la base de datos utilizando JPA
Como vimos en la sección anterior el concepto de Entity
en JPA permite representar objetos persistentes que se guardan en registros de la base de datos.
El interface EntityManager
es el elemento principal de JPA para acceder a la base de datos. Tiene servicios para crear queries, crear objetos persistentes, borrarlos, etc. Las siguientes líneas de código ilustran lo anterior.
...
@PersistenceContext(unitName = "CompanyPU")
EntityManager em;
CompanyEntity company = em.find(CompanyEntity.class, id);
...
Se declara un EntityManager
llamado em.
Luego se utiliza para buscar un objeto CompanyEntity
dado un id
(llave primaria) utilizando el método find
. Este método busca el registro en la base de datos en la tabla COMPANYENTITY
utilizando elid
dado y retorna un objeto de la clase java CompanyEntity
que queda referenciado por la variable company.
La anotación @PersistenceContext
es muy importante porque permite asociar el entity manager con la unidad de persistencia llamada "CompanyPU"
. La configuración de esta unidad de persistencia se hace por fuera del código en un recurso llamado persistence.xml
. Allí se define la información necesaria para conectarse con la base de datos.
Clases de Persistencia y el "Entity Manager"
La decisión de diseño para la capa de persistencia consiste en tener una clase de persistencia por cada entidad. Cada clase de persistencia es EJB sin estado (@Stateless
). La lógica de la aplicación se comunica con estas clases que manipulan las entidades persistentes utilizando un Entity Manager
.
El siguiente diagrama de clases muestra con el ejemplo company
las clases de persistencia. Hay una clase por cada entidad: CompanyPersistence
, DeparmentPersistence
y EmployeePersistence
. Cada clase de persistencia tiene una dependencia hacia la entidad que maneja.
Para responder a los servicios básicos CRUD, cada clase de persistencia tiene los métodos:
Método | Parámetros | Retorno | |
---|---|---|---|
C | create | el objeto de la entidad que se quiere persistir. | retorna el objeto que se persistió incluido el id (llave primaria) que la base de datos generó. |
R | find | id : llave primaria de la entidad que se va a buscar. |
objeto de la entidad correspondiente si la encuentra null si no. |
R | finAll |
retorna la colección de todos los objetos que existen en la base de datos de la entidad correspondiente. | |
U | update |
el objeto de la entidad que se quiere actualizar. | el objeto actualizado. |
D | delete |
id : llave primaria de la entidad que se va a borrar. |
void |
Una clase de persistencia puede tener muchos otros métodos adicionales al CRUD, la mayoría de estos pueden estar relacionados con consultas y cálculos sobre la base de datos. del ejemplo anterior, en la clase CompanyPersistence
tenemos un método que se ocupa de devolver la entidad compañía que coincide con un nombre dado.
Método | Parámetros | Retorno | |
---|---|---|---|
R | findByName | name : nombre de la compañía que se quiere buscar. |
objeto de la entidad correspondiente si la encuentra null si no. |
La clase EmployeePersistence
puede tener métodos que devuelvan el empleado que tiene el salario más alto o el promedio de salarios de todos los empleados o el promedio de salarios por departamento o el departamento que tiene el mayor número de empleados.
Los métodos de las clases de persistencia reciben y devuelven entidades y/o valores simples. La capa de persistencia no maneja DTOs. Esto quiere decir que, en este diseño, no hay ninguna relación entre la capa de persistencia y la capa de recursos. En la mitad de los dos está la lógica de la aplicación.
Los métodos se las clases de persistencia se pueden probar de manera aislada sin los recursos o la lógica. Sólo se necesitan las clases de persistencia las clases de las entidades para realizar las pruebas.
Transacciones
Una transacción define una unidad lógica de trabajo que tiene éxito o no producirá ningún resultado en absoluto. Una transacción distribuida es una transacción donde, por ejemplo, clientes distintos y distribuidos en la red, acceden y actualizan la misma base de datos.
Java Transaction API (JTA) permite a las aplicaciones JEE realizar transacciones distribuidas. Estas transacciones pueden ser:
- Manejadas por el contenedor (Container-Managed Transactions): en este caso el desarrollador no tiene necesidad de escribir código para iniciar o terminar una transacción (commit/rollback). Cuando inicia la ejecución de un método dentro del contenedor, este se ejecuta en la transacción que esté abierta (si no hay ninguna entonces inicia una), la ejecución del método hará parte de la transacción. Se realizará un
commit
de la transacción cuando el método que la inició termine. - Manejadas por el Bean (Bean-Managed Transactions): en este caso el desarrollador define cuando inicia y termina una transacción. Para esto debe usar la interface
javax.transaction.UserTransaction
que tiene los servicios de creación, commit y rollback de las transacciones.
En nuestros ejemplos JEE siempre hemos utilizado transacciones manejadas por el contenedor. Quiere decir que los métodos siempre se ejecutan dentro de una transacción (todo o nada). Esto incluye los métodos que utilizan
EntiyManager
para acceder y/o modificar la base de datos.
Contexto de Persistencia y ciclo de vida de las entidades
Cuando un objeto de una clase anotada con Entity
es creado, este está en un estado que no tiene, todavía, ninguna relación con la base de datos. Cuando el objeto es persistido o cuando un objeto es obtenido de la base de datos, queda en un estado manejado por JPA. Cuando estos objetos son removidos o separados de su manejador (detach) salén del estado Managed
por JTA.
(Imagen tomada de: ObjectDB)
El Contexto de Persistencia es el conjunto de todos los objetos (entidades) que se encuentran en el estado manejado por JTA (Managed
en la figura). Cada Entity Manager
tiene su propio contexto de persistencia.
A estos objetos en el contexto de persistencia les ocurre:
- Acceso Base de datos: Solo hay una representación en memoria para un objeto (entity) dado y si se pregunta por ese objeto y ya está en el contexto no se accede de nuevo a la base de datos.
- Actualizaciones automáticas: Si se cambia el valor de un atributo en uno de estos objetos, ese valor se cambiará automáticamente en la base de datos (sin necesidad de hacer una actualización explícita. Si los objetos tienen atributos que representan relaciones con otros objetos y estos atributos están anotados con el atributo
Cascade.Persist
oCascade.ALL
, también se actualizarán esos objetos relacionados.
Material Complementario
- Arquitectura JPA
Referencias
http://openjpa.apache.org/builds/1.2.3/apache-openjpa/docs/jpa_overview_arch.html