INTRODUCCION
JPA es el acrónimo de Java Persistence API y se podría considerar como el estándar de los frameworks de persistencia.
En JPA utilizamos anotaciones como medio de configuración.
ENTIDADES
Consideramos una entidad al objeto que vamos a persistir o recuperar de una base de datos. Se puede ver una entidad como la representación de un registro de la tabla.
Toda entidad ha de cumplir con los siguientes requisitos:
- Debe implementar la interface Serializable
- Ha de tener un constructor sin argumentos y este ha de ser público.
- Todas las propiedades deben tener sus métodos de acceso get() y set().
Para crear una entidad utilizamos la anotación @Entity, con ella marcamos un POJO como entidad
POJO son las iniciales de «Plain Old Java Object», que puede interpretarse como «Un objeto Java Plano Antiguo». Un POJO es una instancia de una clase que no extiende ni implementa nada en especial.
MAPEOS DE ENTIDADES
En este apartado vamos a ver las anotaciones más utilizadas para mapear cada una de las propiedades de la entidad contra la BBDD.
@Id
Se utiliza para marcar una propiedad como clave primaria tanto si es una PK (Primary Key) simple, de un solo cambio o si forma parte de una PK compuesta, de varios campos.
Como sabemos las PK no pueden aceptar valore repetidos, estos han de ser únicos y no nulos.
En este caso en la clase String está implementado el método equals() por lo que no tenemos que hacer nada más.
Toda clave primaria debe tener implementado este método para poder comprobar si ya existe este valor de la PK en la tabla.
@IdClass
Esta anotación nos sirve para poder anotar una clave primaria compuesta.
En una clase aparte definimos la clave primaria compuesta cumpliendo los siguientes requisitos:
- La clase ha de implementar la interface Serializable.
- En la clase creamos una propiedad por cada campo que forma parte de la PK.
- Estas propiedades deben tener un método get() y set() cada una de ellas.
- También debemos facilitar un constructor sin argumentos.
- Debemos implementar los métodos equals() y hashCode().
En la entidad utilizamos la anotación @IdClass para especificar la clase que utilizamos como clave primaria compuesta.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
public class PersonaPK implements Serializable{ private Long telefono; private String nif; public PersonaPK() { } public PersonaPK(Long telefono, String nif) { this.telefono = telefono; this.nif = nif; } public String getNif() { return nif; } public void setNif(String nif) { this.nif = nif; } public Long getTelefono() { return telefono; } public void setTelefono(Long telefono) { this.telefono = telefono; } @Override public String toString() { return "PersonaPK{" + "telefono=" + telefono + "nif=" + nif + '}'; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final PersonaPK other = (PersonaPK) obj; if (this.telefono != other.telefono && (this.telefono == null || !this.telefono.equals(other.telefono))) { return false; } if ((this.nif == null) ? (other.nif != null) : !this.nif.equals(other.nif)) { return false; } return true; } @Override public int hashCode() { int hash = 5; hash = 41 * hash + (this.telefono != null ? this.telefono.hashCode() : 0); hash = 41 * hash + (this.nif != null ? this.nif.hashCode() : 0); return hash; } } |
En la entidad utilizamos la anotación @IdClass para especificar la clase que utilizamos como clave primaria compuesta.
|
1 2 3 |
@Entity @IdClass(PersonaPK.class) @Table(name="Ejemplo1_PERSONAS") |
@Embeddable
Cada una de las propiedades definida en la entidad, pasará a ser un campo en la tabla.
Esta anotación nos permite embeber una clase. Veámoslo con un ejemplo.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
@Embeddable public class Direccion implements Serializable{ private String calle; private String localidad; @Column(name="codigo_postal") private int cp; public Direccion() { } public Direccion(String calle, String localidad, int cp) { this.calle = calle; this.localidad = localidad; this.cp = cp; } |
La clase Dirección declara tres propiedades calle, localidad y cp. La anotación
@Embeddable especifica que toda propiedad que utilice esta clase como tipo en la entidad, será persistida con este formato.

@Embedded
Con esta anotación hacemos que la propiedad de tipo Dirección se persista según la clase Embeddable creada anteriormente.
Si no utilizásemos la anotación se generaría un campo dirección con el identificador del objeto.
@Table
Mediante esta anotación indicamos el nombre de la tabla donde se persistirán o recuperaran los datos de la entidad.
Si no la utilizásemos coge como nombre de la tabla el nombre de la clase. Según el ejemplo sería Persona.
@Column
Lo mismo ocurre con las columnas, podemos especificar su nombre, tamaño, etc. Cada propiedad de la entidad puede utilizar una anotación @Column para establecer las propiedades de la columna en la tabla.
Si no se utiliza, tomacomo nombre de columna el nombre de la propiedad.
@SecondaryTable
Como vemos en el ejemplo una entidad puede utilizar tablas secundarias para almacenar datos, en el ejemplo el curriculum vitae de la persona se almacena en otra tabla.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
@Entity @IdClass(PersonaPK.class) @Table(name="Ejemplo1_PERSONAS") @SecondaryTable(name="Ejemplo1_CV", pkJoinColumns={@PrimaryKeyJoinColumn(name="id_telefono", referencedColumnName="id_telefono"), @PrimaryKeyJoinColumn(name="id_nif", referencedColumnName="id_nif") } ) public class Persona implements Serializable{ @Id @Column(name="id_telefono", nullable=false) private Long telefono; @Id @Column(name="id_nif", nullable=false) private String nif; private String nombre; private int edad; @Enumerated(EnumType.STRING) private EstadoCivil estado; @Column(length=1) private char sexo; @Embedded private Direccion direccion; @Temporal(TemporalType.DATE) private Date fechaNacimiento; @Lob @Basic(fetch=FetchType.LAZY) @Column(table="Ejemplo1_CV") private String cv; |
Veamos cómo utilizar la anotación @SecondaryTable para definir tablas secundarias. En esta anotación utilizamos dos atributos:
- El atributo name nos permite poner un nombre a la tabla.
- El atributo pkJoinColumns unimos la tabla primaria y la tabla secundaria.
Para esta última operación utilizamos otra anotación @PrimaryKeyJoinColumn cuyos atributos son:
- name; es el nombre del campo generado en la tabla secundaria
- referencedColumnName; especificamos el nombre del campo de la tabla primaria por el cual se unen.
@Enumerated
En Java utilizamos los tipos enumerados. Este tipo de datos no lo reconoce la base de datos, por lo cual, debemos utilizar esta anotación para indicar como persistir una propiedad de tipo enumerado. Lo podemos hacer de dos formas:
- Almacenando su índice; EnumType.ORDINAL
- Almacenando su valor; EnumType.STRING
|
1 2 3 |
public enum EstadoCivil { SOLTERO, CASADO, VIUDO, DIVORCIADO } |
@Lob
Designa la propiedad de un campo como:
- CLOB (Character Large Object); se utiliza para introducir un campo de texto muy grande. En nuestro ejemplo lo utilizamos para el curriculum de la persona.
- BLOB (Binary Large Object); permite almacenar archivos binarios como por ejemplo una imagen.
@Basic
Cuando utilizamos la anotación anterior es recomendable especificar el tipo de recuperación. Esto se denomina el tipo fetch.
En nuestro ejemplo cuando queremos leer una entidad Persona de la BBDD se recuperarán todos los datos referentes a ella.
Ahora bien, es necesario recuperar el curriculum de la persona cuando quizás solo hemos recuperado leído la entidad para conocer su dirección?
Para este fin utilizamos la anotación @Basic, mediante la cual especificamos si recuperamos automáticamente los datos o no.
- FetchType.LAZY; Con este tipo fetch estamos indicando que al recuperar los datos de la entidad, el campo marcado de esta forma no se recupera hasta que no se solicite específicamente.
- FetchType.EAGER; Este otro tipo indica que los datos referentes a este campo se recuperan de forma implícita.
@Temporal
Utilizamos esta anotación para indicar como queremos persistir los objetos de tipo fecha. Puede adoptar las siguientes constantes:
- TemporalType.DATE; almacena la fecha como día, mes y año.
- TemporalType.TIME; se almacena la hora como horas, minutos y segundos.
- TemporalType.TIMESTAMP; almacena fecha y hora juntas.
Hibernate
Hibernate se ha convertido en el Frameworks más utilizado.
Una vez que hemos visto las anotaciones de JPA los conceptos son los mismos para Hibernate, solo que los reconoceremos con otros nombres. A continuación mostramos una tabla donde podemos ver los recursos comunes a ambos frameworks:

Si JPA utiliza el sistema de anotación para realizar mapeos y configuración en Hibernate se sigue utilizando XML. Aunque es cierto que las últimas versiones incluyen la posibilidad de sustituir el código XML por anotaciones en la gran mayoría de proyectos se trabaja de la primera forma.