viernes, 2 de agosto de 2013

Acceso a base de datos con Hibernate y JPA

Hibernate
Si usamos Hibernate como la capa de persistencia de nuestra aplicación es muy probable que lo usemos a través de una aplicación web. Sin embargo, en algún caso como una migración de datos, la generación de algún informe o extracción de datos que no queramos o no nos sea posible hacerla en la aplicación web por el tiempo que tarda o la carga que supone para la aplicación web nos puede interesar usar Hibernate desde una aplicación Java normal lanzada desde linea de comandos.

Para ello deberemos obtener el objeto Session si usamos hibernate directamente o el EntityManager si usamos hibernate a través de JPA. La API de JPA es una capa de abstracción para varias librerías de persistencia, ORM, similares a Hibernate. Si Hibernate nos abstrae de la base de datos, JPA nos abstrae del framework de persistencia que vayamos a usar de forma que podamos reemplazarlo por otro sin realizar ninguna modificación al código JPA. Si ya sabemos usar Hibernate usar JPA no nos supondrá mucha dificultad ya que JPA se basa en gran medida en Hibernate.

En el siguiente ejemplo se puede ver como obtener un EntityManager, eliminar todos los objetos y hacer una búsqueda.

package es.com.blogspot.elblogdepicodev.hibernate;
import java.util.Date;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import es.com.blogspot.elblogdepicodev.hibernate.dao.GenericDAO;
import es.com.blogspot.elblogdepicodev.hibernate.dao.GenericDAOImpl;
public class HibernateJPA {
public static void main(String[] args) {
// Obtener la factoría de sesiones
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("h2");
EntityManager entityManager = entityManagerFactory.createEntityManager();
try {
// Crear una transacción e intertar objetos
entityManager.getTransaction().begin();
GenericDAO dao = new GenericDAOImpl(Producto.class, entityManager);
// Borrar los datos que hubiese
dao.removeAll();
dao.persist(new Producto(
"Tapestry 5",
"Rapid web application development in Java is a comprehensive guide, introducing Apache Tapestry and its innovative approach to building modern web applications. The book walks you through Tapestry 5, from a simple Hello World application to rich Ajax-enabled applications. Written by a core committer this book provides deep insight into the architecture of Tapestry 5. It not only shows you how to achieve specific goals but also teaches you the \"why\". You learn how to build modern, scalable Web 2.0 application with a component-oriented approach. This book also shows how Tapestry brings scripting language productivity within reach of Java developers without sacrificing any of Java's inherent speed and power.",
10l, new Date()));
dao.persist(new Producto(
"Raspberry Pi",
"The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming.",
20l, new Date()));
dao.persist(new Producto(
"Raspberry Pi User Guide",
"The essential guide to getting started with Raspberry Pi computing and programming. Originally conceived of as a fun, easy way for kids (and curious adults) to learn computer programming, the Raspberry Pi quickly evolved into a remarkably robust, credit-card-size computer that can be used for everything from playing HD videos and hacking around with hardware to learning to program! Co-authored by one of the creators of the Raspberry Pi, this book fills you in on everything you need to know to get up and running on your Raspberry Pi, in no time.",
30l, new Date()));
entityManager.getTransaction().commit();
// Hacer una búsqueda y mostrar resultados
List<Producto> productos = dao.findAll();
System.out.printf(" # %1$-20s %2$8s %3$s\n", "Nombre", "Cantidad", "Fecha");
int i = 1;
for (Producto producto : productos) {
System.out.printf("%1$3s %2$-20s %3$8s %4$tR\n", i, producto.getNombre(), producto.getCantidad(), producto.getFecha());
++i;
}
} catch (Exception e) {
entityManager.getTransaction().rollback();
}
entityManager.close();
entityManagerFactory.close();
}
}
En el ejemplo he usado un dao genérico que nos puede servir para cualquier tipo de entidad, aunque el dao contenga operaciones básicas como probablemente sean comunes a todas las entidades que tengamos nos será de gran utilidad. A continuación las anotaciones que debemos usar para la entidad a persistir, nada distinto de lo que haríamos en una aplicación web.

package es.com.blogspot.elblogdepicodev.hibernate;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.apache.solr.analysis.LowerCaseFilterFactory;
import org.apache.solr.analysis.SnowballPorterFilterFactory;
import org.apache.solr.analysis.StandardTokenizerFactory;
import org.hibernate.search.annotations.Analyze;
import org.hibernate.search.annotations.Analyzer;
import org.hibernate.search.annotations.AnalyzerDef;
import org.hibernate.search.annotations.DateBridge;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Index;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.annotations.Parameter;
import org.hibernate.search.annotations.Resolution;
import org.hibernate.search.annotations.Store;
import org.hibernate.search.annotations.TokenFilterDef;
import org.hibernate.search.annotations.TokenizerDef;
@Entity
@Indexed
@AnalyzerDef(name = "textoanalyzer", tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class), filters = {
@TokenFilterDef(factory = LowerCaseFilterFactory.class),
@TokenFilterDef(factory = SnowballPorterFilterFactory.class, params = { @Parameter(name = "language", value = "English") }) })
public class Producto implements Serializable {
private static final long serialVersionUID = 4301591927955774037L;
@Id
@GeneratedValue
private Long id;
@Column(name = "nombre")
@Field(name = "nombre", index = Index.YES, analyze = Analyze.YES)
@Analyzer(definition = "textoanalyzer")
private String nombre;
@Column(name = "descripcion", length = 5000)
@Field(name = "descripcion", index = Index.YES, analyze = Analyze.YES)
@Analyzer(definition = "textoanalyzer")
private String descripcion;
@Column(name = "cantidad")
private Long cantidad;
@Column(name = "fecha")
@Field(index = Index.YES, analyze = Analyze.NO, store = Store.YES)
@DateBridge(resolution = Resolution.DAY)
private Date fecha;
public Producto() {
}
public Producto(String nombre, String descripcion, Long cantidad, Date fecha) {
this.nombre = nombre;
this.descripcion = descripcion;
this.cantidad = cantidad;
this.fecha = fecha;
}
public Long getId() {
return id;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getDescripcion() {
return descripcion;
}
public void setDescripcion(String descripcion) {
this.descripcion = descripcion;
}
public Long getCantidad() {
return cantidad;
}
public void setCantidad(Long cantidad) {
this.cantidad = cantidad;
}
public Date getFecha() {
return fecha;
}
public void setFecha(Date fecha) {
this.fecha = fecha;
}
}
view raw Producto.java hosted with ❤ by GitHub
Si JPA no tuviese alguna funcionalidad que si tuviese hibernate desde JPA podemos obtener el objeto Session con el que acceder a la API de hibernate.

Session session = (Session)entityManager.getDelegate();
Ejecutando la aplicación HibernateJPA obtendremos lo siguiente en la consola:

Como comentaba usar Hibernate en una aplicación Java normal no será lo habitual pero este ejemplo es el primer paso para que explique con un ejemplo simple como resolver otro de los problemas que suele ser habitual en muchas aplicaciones que es como hacer búsquedas sobre los objetos de dominio y no me refiero a usar likes de SQL que tienen varias limitaciones aparte de ser lentas si la tabla de búsqueda tiene muchos registros sino usando Hibernate Search.

Para probar los ejemplos en tu equipo puedes hacerlo con los siguientes comandos:

$ git clone git://github.com/picodotdev/elblogdepicodev.git
$ cd elblogdepicodev/HelloWorldHibernate
$ ./gradlew --daemon build copyToLib
$ ./HibernateJPA.sh
view raw git.sh hosted with ❤ by GitHub
Si te ha interesado esta entrada quizá también te interese como hacer internacionalización (i18n) de campos con Hibernate y como hacer búsquedas de texto completo en objetos de dominio.

Referencia:
Código fuente acceso a base de datos con Hibernate y JPA
http://docs.jboss.org/hibernate/core/3.6/quickstart/en-US/html/hibernate-gsg-tutorial-jpa.html
http://www.manning.com/bauer2/
http://www.apress.com/9781430219569