Buenas tardes,
La idea de este post es ilustrar en un ejemplo completo, una aplicación este configurada mediante anotaciones JPA y la capa de persistencia Hibernate bien ensambladas; mencionando temas como el efecto CASCADE y FETCH en los mapeos (vía anotaciones). Finalmente se dejara el código fuente de este ejemplo para su análisis.
En breve descripción se usan tres tablas con nombres muy descriptivos: cliente, producto, orden y orden_detalle; la idea es que un cliente realiza una orden de algunos productos. Para esto empezamos a definir nuestras clases bean que tienen similares nombres: Cliente, Producto, Orden y OrdenDetalle respectivamente
Cliente.java
package com.corppm.technology.pruebas.entidades;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "cliente")
public class Cliente {
@Id
@Column(name = "id", nullable = false)
private Long id;
@Column(name = "dna", nullable = false)
private String DNA;
@Column(name = "nombre_completo", nullable = false)
private String nombreCompleto;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getDNA() {
return DNA;
}
public void setDNA(String dNA) {
DNA = dNA;
}
public String getNombreCompleto() {
return nombreCompleto;
}
public void setNombreCompleto(String nombreCompleto) {
this.nombreCompleto = nombreCompleto;
}
}
Producto.java
package com.corppm.technology.pruebas.entidades;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "producto")
public class Producto {
@Id
@Column(name = "id")
private Long id;
@Column(name = "nombre", nullable = false)
private String nombre;
@Column(name = "sku", nullable = false)
private String SKU;
@Column(name = "precio", nullable = false)
private Double precio;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getSKU() {
return SKU;
}
public void setSKU(String sKU) {
SKU = sKU;
}
public Double getPrecio() {
return precio;
}
public void setPrecio(Double precio) {
this.precio = precio;
}
}
Orden.java
package com.corppm.technology.pruebas.entidades;
import java.util.Date;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name = "orden")
public class Orden {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "fecha", nullable = false)
private Date fecha;
@ManyToOne(fetch = FetchType.EAGER, optional = false)
private Cliente cliente;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, targetEntity = OrdenDetalle.class)
private List detalles;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Date getFecha() {
return fecha;
}
public void setFecha(Date fecha) {
this.fecha = fecha;
}
public Cliente getCliente() {
return cliente;
}
public void setCliente(Cliente cliente) {
this.cliente = cliente;
}
public List getDetalles() {
return detalles;
}
public void setDetalles(List detalles) {
this.detalles = detalles;
}
}
OrdenDetalle.java
package com.corppm.technology.pruebas.entidades;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name = "orden_detalle")
public class OrdenDetalle {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne()
@JoinColumn(name="orden_id")
private Orden orden;
@ManyToOne(fetch = FetchType.EAGER, optional = false)
private Producto producto;
@Column(name = "cantidad", nullable = false)
private Integer cantidad;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Orden getOrden() {
return orden;
}
public void setOrden(Orden orden) {
this.orden = orden;
}
public Producto getProducto() {
return producto;
}
public void setProducto(Producto producto) {
this.producto = producto;
}
public Integer getCantidad() {
return cantidad;
}
public void setCantidad(Integer cantidad) {
this.cantidad = cantidad;
}
}
En estos mapeos vamos a destacar algunas cuestiones:
- GeneratedValue: Se aplicó solamente a las tablas de orden y orden_detalle, con la estrategia IDENTITY lo que quiere decir que la base de datos es la encargada de asignar un valor incremental (secuencial) en cada registro nuevo. Las otras dos tablas no tiene generación automática por tanto esperan que el id también sea pasado al construir el objeto.
- ManyToOne: Usado en el mapeo de orden pago, dice que existe una referencia de un detalle a su orden padre, y a través de la anotación JoinColumn especificamos la columna de la tabla orden_detalle que tiene la referencia del registro padre en la tabla orden.
- OneToMany: Por el otro lado en la tabla padre hacemos referencia a los detalle mediante esta anotación, especificando con que atributo de la clase hija (mappedBy) debe hacer la referencia en ninguna o todas las operaciones que se propagan desde el padre, por ejemplo al grabar, actualizar y/o borrar. Esta propagación se especifica mediante la propiedad CASCADE en nuestro caso configurado a ALL que instruye a la capa de persistencia de modo que toda aperacion sobre el padre afecte a los hijos (si borro una orden… se borraran sus detalles por ejemplo).
El sigiuente paso es configurar nuestras propiedades de persistencia, para eso y segun la especificacion se necesita definir un archivo llamado persistence.xml en el directorio META-INF situado a su vez en el directorio del compilacion del sistema (o de despoliegue en el caso de que ejecutemos en un contenedor). Vale anotar que para este efecto usamos MySQL como base de datos.
persistence.xml
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"> <persistence-unit name="testunit"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <properties> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.format_sql" value="false"/> <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/> <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/hibernatecascade"/> <property name="hibernate.connection.username" value="pruebas"/> <property name="hibernate.connection.password" value="pruebas"/> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/> <!-- <property name="hibernate.hbm2ddl.auto" value="create"/> --> </properties> </persistence-unit> </persistence>
Sin mucho que comentar contiene las mismas propiedades que necesita una conexion comun, mas una consideracion importante acerca de provider que lo hemos definido como “org.hibernate.ejb.HibernatePersistence”, siendo esta característica el primer punto de ensamblaje entre Hibernate y JPA.
Por otro lado vemos que toda la configuración se envolvió en la etiqueta persistence-unit con nombre “testunit”; esto se debe a que se puede proveer varios punto de conexión que permitirían a una aplicación mucho mas compleja poder manipular los datos de varias bases de datos tranparentemente en la misma aplicación.
Finalmente creamos una clase con main, para ejecutar una simple operación de persistencia de este ejemplo:
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.Query;
import com.corppm.technology.pruebas.entidades.Cliente;
import com.corppm.technology.pruebas.entidades.Orden;
import com.corppm.technology.pruebas.entidades.OrdenDetalle;
import com.corppm.technology.pruebas.entidades.Producto;
public class Test01 {
/**
* @param args
*/
public static void main(String[] args) {
EntityManagerFactory emf = Persistence
.createEntityManagerFactory("testunit");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Query query = em.createQuery("SELECT o FROM Cliente o");
if (query.getResultList().size() == 0) {
poblarDatos(em);
}
Orden orden = new Orden();
orden.setCliente(em.find(Cliente.class, 10L));
orden.setFecha(new Date());
List detalles = new ArrayList();
OrdenDetalle ordenDetalle = new OrdenDetalle();
ordenDetalle.setOrden(orden);
ordenDetalle.setProducto(em.find(Producto.class, 100L));
ordenDetalle.setCantidad(12);
detalles.add(ordenDetalle);
ordenDetalle = new OrdenDetalle();
ordenDetalle.setOrden(orden);
ordenDetalle.setProducto(em.find(Producto.class, 200L));
ordenDetalle.setCantidad(99);
detalles.add(ordenDetalle);
orden.setDetalles(detalles);
em.persist(orden);
em.flush();
em.remove(orden);
tx.commit();
}
private static void poblarDatos(EntityManager em) {
Cliente cliente = new Cliente();
cliente.setId(10L);
cliente.setDNA("1088888888201345");
cliente.setNombreCompleto("Nombre Completo 1");
em.persist(cliente);
Producto producto = new Producto();
producto.setId(100L);
producto.setSKU("SKU001A");
producto.setNombre("Producto A");
producto.setPrecio(23.34);
em.persist(producto);
producto = new Producto();
producto.setId(200L);
producto.setSKU("SKU003B");
producto.setNombre("Producto B");
producto.setPrecio(11.09);
em.persist(producto);
}
}
Lo importante en este código es analizar la salida “output” en consola para que se percaten de que al borrar la orden borra también los detalles, con las instrucciones sql correspondientes” y se mantiene en consistencia a la propiedad CASCADE que explicamos anteriormente.
El link de descarga del codigo es:
hibernatecascade
Espero les sirva
Saludos

