Persistence en Java 2 - Anotaciones POJOs - Java Persistence API

Una entidad JPA (Java Persistence API)  no es más que un POJO que dispone de una serie de anotaciones, que indican qué y cómo se persiste un objeto, es decir, anotaciones para manipular objetos en una base de datos. Estas anotaciones vinculan cada propiedad del POJO con las de la base de datos y establecen un vínculo directo. 

Resumen de Anotaciones principales:

@Entity indica que el objeto es una entidad.
@Table indica la tabla de base de datos que contiene objetos.
@Id, @IdClass, @EmbeddedId se emplean para declarar claves primarias de clases
@Transient hace que una variable no sea tomada por una base de datos.
@Column indica la columna de una @Table
@Basic indica cuando y como se debe cargar un objeto persistido
@OneToOne es usada para crear relaciones entre otras entidades que tengamos de una a una
@OneToMany hace referencia a la asociación de 1-N.
@ManyToMany hace referencia a la asociación N-N.


Anotación @Entity indica que el objeto es una entidad.
La anotación @Table indica la tabla de base de datos que contiene objetos.
Ejemplo de código:

 
@Entity 
@Table(name="PRODUCTOS") 
public class Producto { 
     @Id 
    private String nombreproducto;  
    public Producto() { } 
     //METODOS Getters y Setters  
}
 
Toda entidad debe tener una clave primaria que se debe identificar con la anotación @Id. Además, todas las entidades deben contener un constructor vacío.

Anotaciones @Id, @IdClass, @EmbeddedId se emplean para declarar claves primarias de clases, bien de forma simple @Id o compuesta @IdClass y @EmbeddedId.
En el caso de las claves compuestas se debe definir una clase para representar a la clave primaria, y redefinir el método equals para comprobar que dos instancias de una clase sean iguales.
Veamos un código utilizando @IdClass

 
@Entity 
@IdClass(PersonaId.class) 
public class Persona { 
    @Id 
    private int id; 
    @Id 
    private String nombre;  
    ... 
    public Persona() { } 
    //METODOS Getters y Setters  
} 
 
public class PersonaId { 
     int id; 
    String nombre; 
     public boolean equals(Object o) { 
        // Codigo que comprueba si las dos entidades son iguales 
    } 
}
 
Y ahora un código utilizando @EmbeddedId

 
@Entity 
public class Persona { 
  @EmbeddedId 
  @AttributeOverrides({ 
     @AttributeOverride(name = "id", column = @Column(name = "ID",  
                              nullable = false, precision = 5, scale = 0)), 
    @AttributeOverride(name = "nombre", column = @Column(name = "NOMBRE"
                       nullable = false, length = 50)), 
 }) 
    private PersonaId id; 
    public Persona() { } 
    //Métodos Getters y Setters  
} 

@Embedded 
public class PersonaId { 
     int id; 
    String nombre; 
     public PersonaId() { } 
     public boolean equals(Object o) { 
        // Codigo que comprueba si las dos entidades son iguales 
    } 
} 
 

La anotación @Transient hace que una variable no sea tomada como un campo del origen de la base de datos.
Además tenemos diferentes formas de mapear cada campo indicando nombres de columna y otros valores.

 
@Entity 
@Table(name="PRODUCTOS") 
public class Productos { 
    @Id 
    private String nombreproducto;  
    @Column(name="CATEGORIA", nullable=false) 
    private String categoria; 
    @Column(name="DESCRIPCION", nullable=false) 
    private String descripcion; 
    @Lob 
    @Basic(fetch=FetchType.LAZY) 
    private byte[] imagen; 
    public Productos() { } 
    //Métodos Getters y Setters  
} 
 
La anotación @Column permite definir varias propiedades:
La propiedad name puede especificar el nombre de la columna
La propiedad nullable indica si la columna acepta valores null o no, si no se incluye el valor por defecto es true.
La anotación @Basic sirve para marcar cualquiera de los tipos de datos que son persistidos de forma automática además de las clases que sean Serializables.
La propiedad fetch permite definir cuándo se debe cargar el campo en cuestión.
FetchType.LAZY indica que el campo se va a cargar de forma "perezosa", es decir, el campo se cargará sólo cuando se acceda a su información, esto supone una consulta más para la obtención del campo, pero en casos de gran cantidad de información es muy beneficioso.
El valor FetchType.EAGER indica que el valor será cargado cuando se cargue el resto del objeto.
El valor por defecto de la propiedad fetch es FetchType.EAGER.
La anotación @Lob indica que el contenido de un campo básico será guardado como LOB (Large Object), por ejemplo, utilizamos esta anotación para marcar un campo que contenga un String o caracteres, el framework lo mapeará a una columna CLOB (Character Large Object).
Si es de otro tipo, normalmente byte[], será mapeado a una columna de tipo BLOB (Binary Large Object).
Anotación @Embeddable forman parte del objeto entidad al que pertenecen.

 
@Entity 
@Table(name="PERSONAS") 
public class Persona { 
    @Id 
    private String nombre;  
    @Embedded 
    @AttributeOverrides({@AttributeOverride(name="codigoPostal",  
                                            column=@Column(name="CODIGOPOSTAL")), 
                         @AttributeOverride(name="direccionPostal",  
                                            column=@Column(name="DIRECCIONPOSTAL")) 
    }) 
    private Direccion direccion; 
    public Persona() { } 
    //Métodos Getters y Setters  
} 

@Embeddable  
public class Direccion implements Serializable {  
    private String direccionPostal; 
    private String ciudad; 
    private int codigoPostal; 
    private String pais; 
    public Direccion() { } 
    public boolean equals(Object o) { 
        // Codigo que comprueba si las dos entidades son iguales 
    }   
}
 
Anotación @OneToOne es usada para crear relaciones entre otras entidades que tengamos declaradas:

 
@Entity 
@Table(name="PRODUCTOS") 
public class Producto { 
    @Id 
    private String nombreproducto;  
    @Column(name="DESCRIPCION", nullable=false) 
    private String descripcion; 
    @OneToOne 
    @JoinColumn(name="CATEGORIA _ID", 
        referencedColumnName="CATEGORIA_ID", updatable=false) 
    private Categoria categoria; 
    public Producto() { } 
    //Métodos Getters y Setters  
}  
@Entity 
@Table(name="CATEGORIAS") 
public class Categoria { 
  @Id 
  @Column(name=" CATEGORIA_ID") 
  private int id; 
  @Column(name="NOMBRECATEGORIA", nullable=false) 
  private String nombrecategoria; 
  public Categoria() { } 
  //Métodos Getters y Setters  
}
 
Anotación @OneToMany hace referencia a la asociación de 1-N en la que una entidad tiene dos o más referencias de otra.
Poniendo un ejemplo, podemos tener una tabla de jugador y otra de equipo. Un equipo puede contener varios jugadores, pero un jugador sólo puede estar en un equipo.
Esta relación sería:

 
@Entity 
@Table(name="EQUIPOS") 
public class Equipo { 
    ... 
    private Set<Jugador> jugadores = new HashSet<Jugador>(0); 
    //Métodos Getters y Setters  
   @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY,  
                                   mappedBy = "equipo") 
 public Set<Jugador> getJugadores() { return this.jugadores; } 
 public void setJugadores(Set<Jugador> jugador) { this.jugadores = jugador; }
 ... 
}  
 
La anotación @OneToMany indica que un equipo puede contener varios jugadores.
La anotación @OneToMany tiene una propiedad llamada cascade que define el tipo de operaciones se realizarán cuando se efectúen modificaciones sobre los datos de la entidad.
La propiedad Cascade, puede tomar los siguientes valores:

  • CascadeType.PERSIST: Cuando persistimos una entidad, todas las entidades que contenga esta variable serán persistidas también.
  • CascadeType.REMOVE: Cuando eliminemos una entidad, todas las entidades que contenga esta variable se borrarán del mismo modo.
  • CascadeType.REFRESH: Cuando actualicemos la entidad, todas las entidades que contenga esta variable se actualizarán.
  • CascadeType.MERGE: Cuando hagamos un "merge" de la entidad, todas las entidades que contenga esta variable realizarán la misma operación.
  • CascadeType.ALL: Todas las operaciones citadas anteriormente.
La propiedad mappedBy = "equipo" indica el nombre de la entidad Equipo en el objeto Jugador.
Anotación @ManyToMany hace referencia a la asociación N-N.
Volviendo al ejemplo de equipos y jugadores, la anotación @ManyToOne indicaría que varios jugadores pueden haber sido inscritos en diferentes equipos.
Anotaciones del ciclo de vida:

  • @EntityListeners: Se pueden definir clases listeners con métodos de ciclo de vida de una entidad.
Para hacer referencia a un listener, se debe incluir esta anotación seguido de paréntesis de la clase: @Entity
Listeners(MyListener.class)

  • @ExcludeSuperclassListeners: Indica que ningún listener de la superclase será invocado por la entidad ni por ninguna de sus subclases.
  • @ExcludeDefaultListeners: Indica que ningún listener por defecto será invocado por esta clase ni por ninguna de sus subclases.
  • @PrePersist: El método se llamará justo antes de la persistencia del objeto. Podría ser necesario para asignarle la clave primaria a la entidad a persistir en base de datos.
  • @PostPersist: El método se llamará después de la persistencia del objeto.
  • @PreRemove: El método se llamará antes de que la entidad sea eliminada.
  • @PostRemove: El método se llamará después de eliminar la entidad de la base de datos.
  • @PreUpdate: El método se llamará antes de que una entidad sea actualizada en base de datos.
  • @PostUpdate: El método se llamará después de que la entidad sea actualizada.
  • @PostLoad: El método se llamará después de que los campos de la entidad sean cargados con los valores de su entidad correspondiente de la base de datos. Se suele utilizar para inicializar valores no persistidos.
  • @FlushMode: Modo en que se ejecuta la transacción: FlushModeType.AUTO (por defecto) y FlushModeType.COMMIT.
  • @NamedQuery: Especifica el nombre del objeto query utilizado junto a un EntityManager.
Sus atributos son:

    • name - nombre del objeto query.
    • query - especifica la query a la base de datos mediante lenguaje Java Persistence Query Language (JPQL)
  • @NamedQueries: Específica varias queries como la anterior.

Búsquedas de datos en Entidades

Una vez que tenemos las entidades, podemos realizar consultas de acción sobre los objetos utilizando las clases POJO's.
La forma más sencilla de buscar en un Entity Bean es utilizar el método find(Class, Object) de la interfaz EntityManager.
Este método permite buscar un Entity por su clave primaria.
Para buscar un producto, por ejemplo, podríamos usar el siguiente código:
Producto product = entityManager.find(Producto.class, new Long(1));
El primer parámetro del método find es la clase del Entity que deseamos buscar.
El segundo parámetro, la clave primaria del Entity.
La clase que pasemos como parámetro debe ser anotada con @Entity.

Búsquedas con JPQL

JPA ofrece una API para realizar tanto consultas como operaciones masivas sobre Entity Beans (actualizaciones y eliminaciones).
Esta API involucra tres componentes:

  • Métodos de consulta del EntityManager.
  • La interfaz javax.persistence.Query.
  • El lenguaje JPQL (Java Persistence Query Language).

No hay comentarios:

Publicar un comentario