Guía #13 Tema:: Java Persistence Api (Jpa)
Guía #13 Tema:: Java Persistence Api (Jpa)
Guía #13 Tema:: Java Persistence Api (Jpa)
Escuela: Computación
Asignatura: Java Avanzado
Guía N° 13
Tema: JAVA PERSISTENCE API (JPA)
I. OBJETIVOS.
Que el alumno comprenda los beneficios de las aplicaciones construidas con Java
Persistence API (JPA).
Al utilizar únicamente JDBC se tiene el problema de crear demasiado código para poder ejecutar
una simple consulta. Por lo tanto, para simplificar el proceso de interacción con una base de datos
(select, insert, update, delete), se ha utilizado desde hace ya varios años el concepto de
frameworks ORM (Object Relational Mapping), tales como JPA e Hibernate.
Java Persistence API, mejor conocido como JPA, es el estándar de persistencia en Java. JPA
implementa conceptos de frameworks ORM (Object Relational Mapping).
Como se puede observar en la figura 1, un framework ORM nos permite "mapear" una clase Java
con una tabla de Base de Datos. Por ejemplo, la clase Persona, al crear un objeto en memoria,
podemos almacenarlo directamente en la tabla de Persona, simplemente ejecutando una línea de
código: em.persist(persona). Esto ha simplificado enormemente la cantidad de código a escribir en
la capa de datos de una aplicación empresarial.
Componentes de la persistencia en Java
Además, se definen anotaciones que sirven para establecer mapeos entre tablas y entidades
JPA.
2. Un lenguaje propio para realizar consultas a bases de datos denominado JPQL (Java
Persistence Query Language).
La idea de JPA es trabajar con objetos Java y no con código SQL, de tal manera que podamos
enfocarnos en el código Java. JPA permite abstraer la comunicación con las bases de datos y crea
un estándar para ejecutar consultas y manipular la información de una base de datos.
Todos los atributos de la entidad serán persistentes y se corresponderán con las columnas
de la tabla que mapea. Solo los marcados por la anotación @Transient no lo serán.
La clase no podrá ser declarada como final. Los atributos persistentes no podrán ser
declarados como static o final.
A continuación, se presenta un ejemplo de una clase de entidad con anotaciones:
@Entity
public class Persona implements Serializable {
@Id
@GeneratedValue
private Long personaId;
@Column(nullable = false)
private String nombre;
Se muestra a continuación una tabla que recoge las principales anotaciones que pueden utilizarse
para configurar el mapeo de atributos entre tablas de bases de datos y propiedades del objeto
Entity.
ANOTACIÓN DESCRIPCIÓN
@Column Especifica una columna de la tabla a mapear sobre un atributo de la clase
Entity.
@Transient Identifica a un atributo no persistente que no se mapeará en la base de
datos.
@Id Marca el atributo como identificador (llave primaria). Según JPA, es
obligatorio que exista un atributo de este tipo en cada clase.
@JoinColumn Indica un atributo que actúa como llave foránea en otra entidad. Algunos
de sus atributos son los siguientes:
name: Identifica a la columna que guarda la clave foránea. Es el único
campo obligatorio.
referenced: nombre de la tabla relacionada que contiene la
columna.
Relaciones entre entidades
Al igual que sucede entre las tablas de una base de datos relacional, es posible establecer
relaciones entre diferentes objetos tipo Entity. Estas relaciones implican cardinalidad, es decir el
número de referencias que tiene un objeto en relación con otro. Serán especificadas a través de las
anotaciones:
@OneToOne: cada lado de la relación tiene como máximo una referencia al otro objeto.
En la figura anterior podemos observar una relación de 1 a 1, en la cual una entidad Alumno tiene
una relación con sólo un domicilio, y viceversa, esto es, la cardinalidad entre las entidades es de 1
a 1.
Podemos observar que la clase de Alumno es la que guarda la referencia de un objeto Domicilio,
para mantener una navegabilidad unidireccional y que a partir de un objeto Alumno podamos
recuperar el objeto Domicilio asociado.
Cuando un objeto de Entidad está asociado con una colección de otros objetos de Entidad, es más
común representar esta relación como una relación de Uno a Muchos. Por ejemplo, en la figura
anterior podemos observar la relación de un Instructor el cual puede tener asignados varios Cursos
(relación de 1 a muchos).
Si queremos saber desde la clase Curso qué instructor tiene asociado, deberemos agregar el
mapeo bidireccional (ManyToOne) hacia Instructor. Y en la clase Instructor, se especifica una
relación uno a muchos (OneToMany) hacia una colección de objetos de tipo Curso, el cual puede
ser una estructura de datos Set o un List.
Métodos para gestionar el ciclo de vida de las entidades
public void persist (Object entity): este método graba una entidad en la base de datos y la
marca como gestionada. Bajo este método se configuran las sentencias INSERT que van a
modificar los datos en la BD, pero esto no significa que sean ejecutados automáticamente.
Lo normal es que no se ejecuten hasta que terminen de configurarse todas las sentencias
que forman parte de una transacción.
public <T> T merge (T entity): mezcla una entidad con el contexto de persistencia del
EntityManager, devuelve el resultado de la mezcla y actualiza el estado de la entidad en la
base de datos.
public void flush(): sincroniza el estado de las entidades en el contexto de persistencia del
interfaz EntityManager con la base de datos.
JPQL es un lenguaje de consulta similar a SQL. La principal diferencia es que SQL funciona
con esquemas, tablas y procedimientos almacenados del DBMS pero JPQL los manipula con
objetos de Java y sus respectivos atributos.
SENTENCIA SELECT
SELECT, para especificar el tipo de objetos o valores que deben ser seleccionados.
FROM, para especificar el dominio de datos al que se refiere la consulta.
WHERE, modificador opcional, que puede ser utilizado para restringir los resultados
devueltos por la consulta.
GROUP BY, modificador opcional, que permite agrupar los resultados.
HAVING, modificador opcional, que permite incluir condiciones de filtrado.
ORDER BY, modificador opcional, que permite ordenar los resultados devueltos por la
consulta.
Esta consulta devuelve todas las matrículas, identificadas de forma general por la variable m.
En este caso se devolverán todos los elementos no duplicados de tipo entidad AlumnosEntity
cuyo nombre y primer apellido coincida con el especificado como parámetro.
En este caso se devolverán todos los elementos no duplicados de tipo entidad AlumnosEntity
cuyo nombre comience por “Mari”. De esta forma la partícula LIKE sirve para introducir
patrones de texto.
SELECT COUNT(p) FROM Pelicula p
SELECT p FROM Pelicula p WHERE p.duracion < 120 AND NOT (p.genero =
'Terror')
Gracias a la sentencia anterior se obtendrían todas las instancias de Pelicula con una duración
menor a 120 minutos que no (NOT) son del género Terror.
La sentencia anterior obtiene todas las instancias de Pelicula con una duración entre
(BETWEEN) 90 y (AND) 150 minutos. BETWEEN puede ser convertido en NOT
BETWEEN, en cuyo caso se obtendrían todas las películas que una duración que no (NOT) se
encuentren dentro del margen (BETWEEN) 90-150 minutos.
La sentencia anterior obtiene todas las instancias de Pelicula cuyo título sea como
(LIKE) El% (el símbolo de porcentaje es un comodín que indica que en su lugar puede haber
entre cero y más caracteres). Resultados devueltos por esta consulta incluirían películas con un
título como El Renacido, El Pianista, o si existe, El. El otro comodín aceptado por LIKE es el
carácter “guion bajo” (_), el cual representa un único carácter indefinido (ni cero caracteres ni
más de uno; uno y solo uno).
Existen otras cláusulas que pueden ser utilizadas en las consultas como son:
NULL: condición de nulidad en consultas, por ejemplo, para devolver relaciones que no
existen entre dos entidades dadas.
EMPTY: es una expresión condicional para indicar que un elemento está vacío.
BETWEEN-AND: sirve para imponer condiciones en las consultas en un rango de
valores.
Operadores de comparación <, >, =: sirven para establecer comparaciones con el
modificador WHERE.
Y otras como:
SENTENCIA UPDATE
Permite actualizar valores almacenados por la entidad. Puede incluir de forma opcional el
modificador WHERE. Se ilustra con un ejemplo:
SENTENCIA DELETE
Permite borrar un registro completo correspondiente a una entidad. Al igual que UPDATE, puede
incluir de forma opcional el modificador WHERE. Algunos ejemplos a continuación:
Las consultas expresadas en JPQL serán ejecutadas a través del interfaz EntityManager y los
métodos createX como los siguientes:
1. Crear una base de datos MySQL con nombre “registro_estudiantes”. Dentro de esta bd
deberá crear una tabla llamada “estudiantes” con la siguiente estructura:
PK
2. Abrir netbeans para crear un nuevo proyecto web (Web Application) con nombre
“Practica_JPA”.
Asegúrese de seleccionar Apache Tomcat como servidor web y la versión 6 del JEE.
En la siguiente pantalla Netbeans nos preguntará si vamos a utilizar alguno de los frameworks que
tiene disponibles. Deberá seleccionar el framework “Java Server Faces”.
sv.edu.udb.www.entities
sv.edu.udb.www.managed_beans
sv.edu.udb.www.model
sv.edu.udb.www.utils
Nombres de la clase de
entidad a generar
EstudianteEntity.java
package sv.edu.udb.www.entities;
import java.io.Serializable;
import java.math.BigDecimal;
import javax.persistence.Basic;
import javax.persistence.Entity; Consultas nombradas creadas
import javax.persistence.Id;
por JPA las cuales podemos
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery; utilizar posteriormente.
import javax.persistence.Table;
@Entity
@Table(name = "estudiantes")
@NamedQueries({
@NamedQuery(name = "EstudianteEntity.findAll", query = "SELECT e FROM
EstudianteEntity e"),
@NamedQuery(name = "EstudianteEntity.findByCarnet", query = "SELECT e FROM
EstudianteEntity e WHERE e.carnet = :carnet"),
@NamedQuery(name = "EstudianteEntity.findByNombres", query = "SELECT e
FROM EstudianteEntity e WHERE e.nombres = :nombres"),
@NamedQuery(name = "EstudianteEntity.findByApellidos", query = "SELECT e
FROM EstudianteEntity e WHERE e.apellidos = :apellidos"),
@NamedQuery(name = "EstudianteEntity.findByEdad", query = "SELECT e FROM
EstudianteEntity e WHERE e.edad = :edad"),
@NamedQuery(name = "EstudianteEntity.findByCum", query = "SELECT e FROM
EstudianteEntity e WHERE e.cum = :cum"),
@NamedQuery(name = "EstudianteEntity.findByGenero", query = "SELECT e FROM
EstudianteEntity e WHERE e.genero = :genero"),
@NamedQuery(name = "EstudianteEntity.findByCarrera", query = "SELECT e
FROM EstudianteEntity e WHERE e.carrera = :carrera")})
public class EstudianteEntity implements Serializable {
public EstudianteEntity() {
}
@Override
public int hashCode() {
int hash = 0;
hash += (carnet != null ? carnet.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields
are not set
if (!(object instanceof EstudianteEntity)) {
return false;
}
EstudianteEntity other = (EstudianteEntity) object;
if ((this.carnet == null && other.carnet != null) || (this.carnet !=
null && !this.carnet.equals(other.carnet))) {
return false;
}
return true;
}
@Override
public String toString() {
return "sv.edu.udb.www.entities.EstudianteEntity[ carnet=" + carnet +
" ]";
}
}
6. Proceda a crear una clase llamada “JpaUtil” dentro del paquete sv.edu.udb.www.utils.
Ubique el siguiente código fuente dentro de la clase.
package sv.edu.udb.www.utils;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
Nombre de la unidad
public class JpaUtil {
de persistencia
private static final EntityManagerFactory emFactory;
static {
emFactory = Persistence.createEntityManagerFactory("Practica_JPA_PU");
}
public static EntityManager getEntityManager() {
return emFactory.createEntityManager();
}
}
package sv.edu.udb.www.model;
import sv.edu.udb.www.utils.JpaUtil;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction; Esta consulta nombrada está
import javax.persistence.Query; en la clase EstudianteEntity.
import sv.edu.udb.www.entities.EstudianteEntity; Siéntase libre de agregar sus
propias consultas
public class EstudiantesModel {
nombradas dentro de las
public List<EstudianteEntity> listarEstudiante() { clases de entidad.
//Obtengo una instancia de EntityManager
EntityManager em = JpaUtil.getEntityManager();
try {
Query consulta = em.createNamedQuery("EstudianteEntity.findAll");
//El método getResultList() de la clase Query permite obtener
// la lista de resultados de una consulta de selección
List<EstudianteEntity> lista = consulta.getResultList();
em.close();// Cerrando el EntityManager
return lista;
} catch (Exception e) {
em.close();
return null;
}
}
8. Proceda a crear una clase llamada “JsfUtil” dentro del paquete sv.edu.udb.www.utils.
Ubique el siguiente código fuente dentro de la clase.
package sv.edu.udb.www.utils;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
9. Hasta este punto, puede consultar con su docente como crear una clase de Test para
realizar las pruebas respectivas, adicional a eso consultar como utilizar la consola de JPQL,
que el IDE de Netbeans proporciona.
package sv.edu.udb.www.managed_beans;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import sv.edu.udb.www.entities.EstudianteEntity;
import sv.edu.udb.www.model.EstudiantesModel;
import sv.edu.udb.www.utils.JsfUtil;
@ManagedBean
@RequestScoped
public class EstudianteBean {
12. Ubique la carpeta “resources” proporcionada por su docente, dentro del directorio Web
Pages de su proyecto.
13. Agregue el JAR de JSTL a las librerías de su proyecto.
14. Agregue una nueva JSF page a su proyecto. El nombre de la página deberá ser
“registroEstudiantes.xhtml” y deberá estar colocada en la raíz del directorio web pages.
La página debe tener el siguiente contenido:
</h:panelGrid>
<h:commandButton value="Guardar" styleClass="btn btn-success"
action="#{estudianteBean.guardarEstudiante()}" style="margin-right:
10px"/>
<h:commandButton type="reset" value="Limpiar" styleClass="btn btn-danger"/>
</h:form>
</div>
</div>
<div class="row">
<h:form >
<h:dataTable styleClass="table" value="#{estudianteBean.listaEstudiantes}"
var="est" rendered="#{estudianteBean.listaEstudiantes.size()>0}">
<h:column>
<f:facet name="header">
<h:outputLabel value="Carnet"/>
</f:facet>
<h:outputText value="#{est.carnet}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputLabel value="Nombre"/>
</f:facet>
<h:outputText value="#{est.nombres}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputLabel value="Apellido"/>
</f:facet>
<h:outputText value="#{est.apellidos}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputLabel value="Carrera"/>
</f:facet>
<h:outputText value="#{est.carrera}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputLabel value="Edad"/>
</f:facet>
<h:outputText value="#{est.edad}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputLabel value="CUM"/>
</f:facet>
<h:outputText value="#{est.cum}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputLabel value="Genero"/>
</f:facet>
<h:outputText value="#{(est.genero== 'm'.charAt(0))? 'Masculino' :
'Femenino'}"/>
</h:column>
</h:dataTable>
</h:form>
</div>
</div>
<h:outputScript>
<c:if test = "#{not empty flash.exito}" >
alertify.success('#{flash.exito}');
</c:if>
</h:outputScript>
</h:body>
</html>
15. Hasta este punto la aplicación debería ser capaz de registrar nuevos alumnos y mostrar los
alumnos registrados mediante un datatable. La página creada debería lucir de la siguiente
manera:
if (modelo.eliminarEstudiante(carnet) > 0) {
JsfUtil.setFlashMessage("exito", "Estudiante eliminado
exitosamente");
}
else{
JsfUtil.setErrorMessage(null, "No se pudo borrar a este alumno");
}
return "registroEstudiantes?faces-redirect=true";
Agregar una columna al datatable que muestra la información. En esta nueva columna
colocaremos un botón que permitirá borrar los datos de un estudiante.
function confirmarEliminacion(){
return confirm("¿Está seguro que desea eliminar este estudiante");
}
Implemente la operación “modificar” en el ejemplo de la guía. Para esta tarea, ya tiene los
métodos listos en la clase EstudiantesModel, por tanto, únicamente deberá realizar los siguientes
cambios:
o Agregar un botón “modificar” a cada uno de las filas del datatable. Al pulsar este
botón, los datos del estudiante seleccionado deben colocarse en el formulario de la
parte superior para su edición.