Usando JPA e Hibernate en un ejemplo completo

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

J2EE - JEE, Uncategorized , , , , Leave a comment

Can’t connect to local MySQL server through socket ‘/tmp/mysql.sock’

Hola,
Decidí desarrollar este post en vista de que este error varia de acuerdo a “en donde” el motor MySQL busca el archivo de extensión sock, es decir encontraremos casos con la misma descripción pero buscando el archivo en /etc/mysql/mysqld.sock por ejemplo. Lo cierto es que por varias razones de las instalaciones la ruta no esta del todo estandarizada, por tanto les recomiendo crear un link a su archivo físico sock de manera que el cliente de mysql lo ubique con su desenvolvimiento natural. Implica entonces el no modificar archivos como el my.cnf o php.ini o cualquier otro,
en mi caso el cliente lo busca en el directorio tmp con el nombre mysql.sock, así que lo primero es averiguar donde esta mi real archivo con una instrucción como:

find / -name "*.sock"

Seguramente hallarán uno con nombre mysql o mysqld en algún directorio relacionada a MySQL, en mi caso en /var/run/mysql/mysqld.sock. Nos queda entonces crear el link en /tmp para que mi cliente no tenga problemas al resolverlo:

ln -s /tmp/mysql.sock /var/run/mysql/mysqld.sock

Tan simple como eso volvemos a intentar la ejecución de nuestro cliente y caminaremos sin problemas.
Espero que esto les ayude con este inconveniente y no pasen tiempo como yo

BDD Topics, PHP Topics , , , Leave a comment

Interpretación commillas y comilla simple en Tomcat – escaped problem

Holas,

Estuve trabajando con una aplicación de hace algún tiempo y al cambiar de versión de tomcat para el deployed de la misma me tope con: ” quoted with ” which must be escaped when used within the value’ “, y la solución no es en realidad re-codificar todas las jsp con problemas… ya que podría suponer un esfuerzo demasiado alto en comparación al “porque?” estamos dando mantenimiento a la app. De todos modos les dejo un link, del cual básicamente aprendí a configurar los parámetros necesarios para decirle al Tomcat que los interprete sin tanta restricción

http://blog.neodoo.es/maznar/jboss/solucionar-el-error-is-quoted-with-%E2%80%9D-which-must-be-escaped-when-used-within-the-value-en-tomcat/

Espero que les sirva
Saludos

Other Topics , , , , Leave a comment

TIP: java.lang.UnsatisfiedLinkError: no ocijdbc10 in java.library.path en Linux

Holas,

Les dejo y me dejo un dato acerca de este problema que me saco un dolor de cabeza. Como ya saben mucha diferencia de OCI a THIN en Oracle, y bien, debíamos trabajar con OCI por un tema de balanceo. La aplicación no corrió satisfactoriamente dando el error mencionado.
Muchos lugares en Internet hablan de configurar LD_LIBRARY_PATH y es una alternativa pero en mi experiencia mejor hagan lo siguiente:

Descargen el “instantclient 10″ de Oracle
Descomprimanlo en cualquier directorio
Copien todos los lib* en su directorio del jre, algo como “/usr/lib/java/jdk1.5.0_22/jre/lib/i386″
Asegúrense de que están usando o el classes12.zip o el ojdbc14.jar en su proyecto que vinieron en el “instantclient 10″
Arranque su proyecto y listo

Aunque no lo he probado segura funcionara con “no ocijdbc9″ ya que es el mismo tema de las librerías.

Espero que les ayude
Saludos

BDD Topics, Java Topics , , , , , , Leave a comment

Manejar OnClick en una Celda de un Grid GWT

Holas,

Esta vez con un ejemplo de como utilizar el onclick sobre una celda de un gris que estemos construyendo con GWT, y me refiero al Grid de GWT como tal y no de SmartGWT u otros.

Bien, como saben para dibujar una celda de un Grid normalmente utilizamos la instrucción:

grid.setWidget(0, 0, new Label("Id"));

Que nos permite dibujar un Label por ejemplo, en una determinada posición (fila,columna), en general como podríamos pensar para darle un funcionalidad de onclick a este Label podríamos hacer algo como:

Label id = new Label(material.getId().toString());
id.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
Window.alert("Hola");
}
});
grid.setWidget(0, 0, id);

Ok, para los que ya lo han hecho notaron que no funciona y al hacer click en el Label no sucede nada… como si no hubiéramos declarado el Handler. En realidad el Gris requiere de otra codificación pero aun así es totalmente viable configurar el evento. Veamos el siguiente código:


grid.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
HTMLTable.Cell cell = grid.getCellForEvent(event);
//Window.alert(cell.toString());
if (cell.getCellIndex() == 0 && cell.getRowIndex() == 0) {
//hacer algo
}
}
});

Como puede observar el evento maneja el click sobre el Grid y dentro de el mismo se puede determinar en que posición ocurrió el mismo: fila->cell.getRowIndex() y columna->cell.getCellIndex(). Si ustedes prueban el código observaran que al dar clic en la celda esperada este Handler definitivamente ejecutara lo especificado en el método.

No he encontrado una forma mas simple de manejar onclick sobre celdas pero esta forma va bien y funciona.

Espero que les sirva
Saludos

AJAX , , , Leave a comment

Superponer paneles o “bring to front” – gwt y smartgwt

Holas,

Al usar el ListGrid de smartgwt tuve problemas con ciertos popups y dialogbox al presentar mensajes, durante la operación de las pantallas; el tema fue que estos panales (divs) se presentaban tras el Listgrid u otros componentes dejando muy mal la presentación.

Revisando un poco me tope que tiene que ver con la propiedad z-index de CSS, que es el orden de como se sitúan los divs en una pantalla. La clave esta en setear esta propiedad con el valor mas alto posible.

Les dejo dos links de referencia

http://forums.smartclient.com/showthread.php?t=6208

http://www.sidar.org/recur/desdi/mcss/manual/ejemplos/zindex.html

Espero les sirva
Gracias

AJAX, Uncategorized , , , , Leave a comment

Como manejar la tecla tab en gwt

Holas,

Me encontre en la necesidad de dar una cierta accion y no la esperada al detectar un tab sobre un field del formulario “key press tab” pero este evento no es detectable al configurar un handle para el keypress del field; sino solo al manejar el keydown.

Les dejo un codigo de ejemplo y un link de mas informacion

doubleBoxUnidades.addKeyDownHandler(new KeyDownHandler() {

@Override
public void onKeyDown(KeyDownEvent event) {
if (event.getNativeKeyCode() == KeyCodes.KEY_TAB) {
//com.google.gwt.user.client.Window.alert("tab presionado.");
doubleBoxPrecioUnitario.setFocus(true);
}
}
});

http://albertattard.blogspot.com/2009/11/capturing-tab-key-in-gwt-textarea.html

Espero les sirva
Saludos

AJAX , , Leave a comment

@PersistenceUnit y @PersistenceContext + no transaction is in progress

Holas,

Voy a exponer unos conceptos básicos a la hora de elegir cual inyección usar en nuestros desarrollos, especialmente para aquellos que están dando los primeros pasos con JPA (de paso me dejo una base de conocimiento).

Lo primero que hay que entender es que JPA es un tecnología de la SUN que no necesita de una Servidor Web o Servidor de Aplicaciones para funcionar, de la cual existen algunas implementaciones entre ellas las mas notables como OpenJPA e Hibernate.

Por este motivo podríamos implementar nuestra capa de persistencia, con JPA, en una aplicación stand-alone, en un cliente-servidor o en ambientes web con servidores de web/aplicaciones

Dicho y entendiendo un conocimiento básico de JPA siempre veremos los archivos:
- persistence.xml (donde definimos nuestro/s persistence-unit)
- orm.xml o anotaciones (donde realizamos el mapeo de nuestras clases (conocidas tambien como DTO) a nuestras tablas de la bdd)
- EntityManager y EntityManagerFactory (con los que accesamos a las tablas para operaciones CRUD)

Bien vamos a suponer que tenemos configurados los dos primeros puntos, por tanto ya definimos las clases, las conexiones y los parámetros generales con nuestra bdd. En este punto estamos en un método de nuestros objetos de negocio que intenta escribir datos en la bdd, para la cual necesitamos la referencia de nuestro EntityManager.

Para esto podemos:

Usar @PersistenceUnit y obtener una instancia de EntityManagerFactory y de aqui crear un EntityManager con el cual podemos manejar las clases mapeadas a la tablas

@PersistenceUnit EntityManagerFactory entityManagerFactory;
EntityManager entityManager = entityManagerFactory.createEntityManager()
tx.begin(); //Iniciamos una transaccion para ejecutar las operaciones a la bdd
entityManager.find(..........
...
utx.commit(); //Comprometemos los cambios en la base

Todo va ok pero significa que cada ves que se ejecute este código, se crea una nueva instancia del EntityManager, y las operaciones que realizamos con esta las adherimos a la transaccion en cuestion. Esto no es malo, pero significa que ustedes deben controlar a nivel de su aplicacion cuando crean un transaccion, cuando la comprometen o la anulan y como alinear cada EntityManager creado… OHH y que pasa si no le dieron transaccion? entonces va a ver el famoso “no transaction is in progress” o que pasa si crearon una nueva en el flujo de ejecución de varios métodos de negocio? pues si la una falla la otra si termina y la base queda inconsistente o la una no ve el dato de la otra y graba valores inconsistentes etc. etc. etc.
Esto es inaceptable por ejemplo a escribirlo sobre atributos de servlets, pues recordemos que funcionan por hilos y sus EntityManagers van a ser el mismo dando una mezcla de commits y rollbacks inesperado.
Pero que hay si es una app del tipo stand-alone? pues bien…. me parece la opcion correcta, pero para este momento ya pueden decirdir ustedes :-)

Usar @PersistenceContext y obtener una instancia de “EntityManager” con el cual podemos manejar las clases mapeadas a la tablas

@PersistenceContext EntityManager entityManager;
// No hay begin del tx!!!!
entityManager.find(..........
...
// No commit tampoco!!!

Aja!!!, si nosotros no manejamos la persistencia significa que la delegamos a un container, como por ejemplo al Tomcat (servlet/server) un jboss (app/server) o spring (:-)). Ok el cambio es evidente no sabemos si sabremos cuando inicia o termina la transacción ni que entity manager nos entregaron, pero no nos preocupa porque lo hace un contenedor. Obviamente no justifica montar a o en un contenedor una app stand-alone, pero si es el caso ya podemos decidir que hacer.

en el caso de Spring hay un soporte total para JPA y estos comportamientos y no estamos hablando de usar un server, por lo que me parece (como en todo) la mejor opción en el caso supuesto.

Por ultimo les dejo dos lecturas complementarias que hablan de PersistenceUnit y PesistenceContext, y el link de como tratar el tema un poco mas “deep” con Spring.

http://javaevangelist.blogspot.com/2011/01/persistenceunit-vs-persistencecontext.html

http://static.springsource.org/spring/docs/3.1.0.M1/spring-framework-reference/html/orm.html#orm-jpa-setup

Espero de verdad les sirva, gracias.

J2EE - JEE , , , , , Leave a comment

JPA Generic Type Extraño Error

Holas,

No es un tutorial solo quiero dejarles y dejarme un TIP cuando sucede lo siguiente:

- Tiene una tabla ejm Menu y su DTO con id tipo long y demás campos
- Tiene un dao en el estilo DAO
- Tienen un JPA en el estilo MenuImplJPA extends DAOImpl

implements MenuJPA.
- Hacen en sus business methods algo como menuDAO.findByPK(“1″) que devuelve algo mediante JPQL 

Bien,

El primer error les dirá que le están pasando un String al JPQL y esperaba un long, entonces cambian por menuDAO.findByPK(1L)

Ok, no da error “TODO BIEN”

FALSO, el objeto les devuelve solo con contenido para el atributo id y el resto de atributos del la clase Menu(DTO) están nulos…… sabes por que?

Fíjense que la declaración de la interfaz dao y su implementación la declararon así:

MenuImplJPA extends DAOImpl<Menu, String> implements MenuJPA

lo que esta mal ya que el id es un Long y le pusieron String pero en compilación y ejecución no les dice NADASSS y solo se ve un objeto vacío. Obviamente la solución es fijarse bien en el DAO creado para menu que debería ser algo como

MenuImplJPA extends DAOImpl<Menu, Long> implements MenuJPA

Y listop, verán que el objeto ya tiene todos los datos

Este error es escondido y lo complejo es que no da un excepción ,,, lo que me llevo a releer el código varias veces tomando en cuenta que no solo tengo un DTO sino como muchos…

Bueno espero les pueda ayudar en algún momento

Saludos

J2EE - JEE , , , Leave a comment

Error Your preferences can not be read en Google Chrome Linux

Holas,

En varios ambientes inclusive en el mio tuve este error y pues al ser tan repetitivo le dejo la solución.

Primero hay un directorio /home/miusuario/.config/google-chrome , ingresa alli y dale un ls -l . Vas a observar que uno de los archivos “Locale State” pertenece al root, lo cual es un problema ya que tu navegador no puede acceder a el y siempre te presenta el error en mención. Así que dale un…

chown miusuario:miusuario Local\ State

como root, y tienes el primer paso; pero hay otro archivo…. cuando le diste ls -l hay un directorio llamado Default es decir /home/miusuario/.config/google-chrome/Default , alli observaras al archivo Preferences con el mismo caso, así que dale un

chown miusuario:miusuario Preferences

Y listo!!! reinicia el chrome y todo estará bien.

Espero les sirva
Saludos

Other Topics Leave a comment