

- JDBC: es la API que viene integrada en la propia plataforma Java sin necesidad de ninguna librería adicional exceptuando el driver JDBC para acceder a la base de datos. Mediante esta opción se tiene total flexibilidad y evita la abstracción y sobrecarga de los sistemas como Hibernate y JPA. Se trabaja con el lenguaje SQL de forma directa y este lenguaje puede varia en algunos aspectos de una base de datos a otra con lo que para migrar a otra base de datos puede implicar reescribir las SQL de la aplicación. Su utilización de forma directa no es tan habitual aunque en casos que se necesite acceder de forma masiva a los datos puede se útil para evitar la sobrecarga o complejidad que añaden sistemas como Hibernate o JPA.
- Hibernate: el modelo relacional de las bases de datos es distinto del modelo de objetos del los lenguajes orientados a objetos. Los sistemas ORM como Hibernate tratan de hacer converger el sistema relacional hacia un modelo más similar al modelo de objetos de lenguajes como Java, de forma que trabajar con ellos sea como trabajar con objetos. En ORM como Hibernate normalmente no se trabaja a nivel de SQL como con JDBC sino que se trabaja con objetos (POJO), las consultas devuelven objetos, las relaciones se acceden a través de propiedades y los borrados, actualizaciones y inserciones se realizan usando objetos y métodos. Los objetos POJO incluyen anotaciones que le indican a Hibernate cual es la información a persistir y las relaciones con otros POJO. Como hibernate dispone de esta información en base a ella puede recrear o actualizar las tablas y los campos necesarios según la definición de esas anotaciones. El ORM es encarga de traducir las acciones a las SQL entendidas por el sistema relacional, esto proporciona la ventaja adicional de que el ORM puede generar las sentencias SQL adaptadas a la base de datos utilizada. De esta forma se podría cambiar de una base de datos a otra sin realizar ningún cambio en la aplicación o con pocos cambios comparado con los necesarios usando JDBC. Con Hibernate se puede emplear un lenguaje de consulta similar a SQL pero adaptado al modelo orientado a objetos, el lenguaje es HQL.
- JPA: es una especificación de Java que define una API común para los sistemas ORM. Con JPA podríamos cambiar de proveedor ORM sin realizar ningún cambio en la aplicación. JPA se ha basado en gran parte en Hibernate y su forma de trabajar es similar, el lenguaje HQL también es similar pero denominado JPQL.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0"> | |
<persistence-unit name="h2"> | |
<provider>org.hibernate.ejb.HibernatePersistence</provider> | |
<properties> | |
<property name="hibernate.connection.driver_class" value="org.h2.Driver"/> | |
<property name="hibernate.connection.url" value="jdbc:h2:mem:test"/> | |
<property name="hibernate.connection.username" value="sa"/> | |
<property name="hibernate.connection.password" value="sa"/> | |
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/> | |
<property name="hibernate.hbm2ddl.auto" value="update"/> | |
<!-- Hibernate Search --> | |
<property name="hibernate.search.default.directory_provider" value="filesystem"/> | |
<property name="hibernate.search.default.indexBase" value="indexes"/> | |
</properties> | |
</persistence-unit> | |
</persistence> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
dependencies { | |
compile 'org.apache.tapestry:tapestry5-annotations:5.3.7' | |
compile 'org.apache.tapestry:tapestry-core:5.3.7' | |
compile 'org.apache.tapestry:tapestry-beanvalidator:5.3.7' | |
compile 'org.apache.tapestry:tapestry-jpa:5.3.7' | |
compile 'org.hibernate:hibernate-core:4.2.1.Final' | |
compile 'org.hibernate:hibernate-validator:5.0.1.Final' | |
compile 'org.hibernate:hibernate-entitymanager:4.2.1.Final' | |
compile 'com.h2database:h2:1.3.171' | |
providedCompile 'javax.servlet:servlet-api:2.5' | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static void bind(ServiceBinder binder) { | |
binder.bind(ValidationDecoratorFactory.class, AppValidationDecoratorFactory.class).withId("AppValidationDecoratorFactory"); | |
binder.bind(ProductoDAO.class, ProductoDAOImpl.class); | |
} | |
public static void contributeBeanValidatorSource(OrderedConfiguration<BeanValidatorConfigurer> configuration) { | |
configuration.add("AppConfigurer", new BeanValidatorConfigurer() { | |
public void configure(javax.validation.Configuration<?> configuration) { | |
configuration.ignoreXmlConfiguration(); | |
} | |
}); | |
} | |
/** | |
* Los servicios con una interfaz *DAO (es necesario que sea una interfaz) | |
* soportan las anotaciones de transaccionalidad. | |
*/ | |
@Match("*DAO") | |
public static void adviseTransactionally(JpaTransactionAdvisor advisor, MethodAdviceReceiver receiver) { | |
advisor.addTransactionCommitAdvice(receiver); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package es.com.blogspot.elblogdepicodev.tapestry.jpa.entities; | |
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 javax.validation.constraints.Max; | |
import javax.validation.constraints.Min; | |
import javax.validation.constraints.NotNull; | |
import org.hibernate.validator.constraints.Length; | |
@Entity | |
public class Producto implements Serializable { | |
private static final long serialVersionUID = 4301591927955774037L; | |
@Id | |
@GeneratedValue | |
private Long id; | |
@NotNull | |
@Length(min = 3, max = 100) | |
@Column(name = "nombre", length = 100) | |
private String nombre; | |
@NotNull | |
@Length(min = 0, max = 5000) | |
@Column(name = "descripcion", length = 5000) | |
private String descripcion; | |
@NotNull | |
@Min(value = 0) @Max(value = 1000) | |
@Column(name = "cantidad") | |
private Long cantidad; | |
@NotNull | |
@Column(name = "fecha") | |
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; | |
} | |
} |
El contenedor de dependencias se encargará en el momento que necesite construir una instancia del servicio DAO inyectarle en el constructor los parámetros necesarios. En este caso una de las clases principales de la API JPA que es EntityManager. Una vez con la referencia a entityManager trabajaríamos usando sus métodos para realizar las consultas y operaciones que necesite proporcionar el DAO.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package es.com.blogspot.elblogdepicodev.tapestry.jpa.dao; | |
import java.util.List; | |
import javax.persistence.EntityManager; | |
import javax.persistence.Query; | |
import javax.persistence.criteria.CriteriaBuilder; | |
import javax.persistence.criteria.CriteriaQuery; | |
import javax.persistence.criteria.Root; | |
import es.com.blogspot.elblogdepicodev.tapestry.jpa.entities.Producto; | |
import es.com.blogspot.elblogdepicodev.tapestry.jpa.misc.Pagination; | |
public class ProductoDAOImpl implements ProductoDAO { | |
private EntityManager entityManager; | |
public ProductoDAOImpl(EntityManager entityManager) { | |
this.entityManager = entityManager; | |
} | |
@Override | |
public Producto findById(Long id) { | |
return entityManager.find(Producto.class, id); | |
} | |
@Override | |
public List<Producto> findAll(Pagination paginacion) { | |
CriteriaBuilder cb = entityManager.getCriteriaBuilder(); | |
CriteriaQuery<Producto> cq = cb.createQuery(Producto.class); | |
Root<Producto> root = cq.from(Producto.class); | |
cq.select(root); | |
if (paginacion != null) { | |
cq.orderBy(paginacion.getOrders(root, cb)); | |
} | |
Query q = entityManager.createQuery(cq); | |
if (paginacion != null) { | |
q.setFirstResult(paginacion.getStart()); | |
q.setMaxResults(paginacion.getEnd() - paginacion.getStart() + 1); | |
} | |
return q.getResultList(); | |
} | |
@Override | |
public void persist(Producto producto) { | |
entityManager.persist(producto); | |
} | |
@Override | |
public void remove(Producto producto) { | |
entityManager.remove(producto); | |
} | |
@Override | |
public void removeAll() { | |
Query query = entityManager.createQuery("delete from Producto"); | |
query.executeUpdate(); | |
} | |
} |
Si quisiésemos hacer rollback en una excepción checked deberíamos hacerlo manualmente capturando la excepción y lanzando una unchecked aunque si es la opción que queremos por defecto podríamos implementar una anotación para tratar la transaccionalidad de esta manera y de forma declarativa en todos los casos.
Una vez que tenemos el servicio DAO basta con inyectarlo en la clase de una página o componente y hacer uso de él. En la siguiente página se muestra un listado de productos donde con un botón se pueden eliminar y con otro se pueden crear nuevos. En la clase de la página se hace uso del DAO para buscar las entidades, crear una nueva con un nombre y descripción aleatoria y eliminar un producto.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package es.com.blogspot.elblogdepicodev.tapestry.jpa.pages.admin; | |
import java.util.Date; | |
import java.util.List; | |
import java.util.UUID; | |
import org.apache.tapestry5.ComponentResources; | |
import org.apache.tapestry5.SymbolConstants; | |
import org.apache.tapestry5.annotations.Cached; | |
import org.apache.tapestry5.annotations.Property; | |
import org.apache.tapestry5.beaneditor.BeanModel; | |
import org.apache.tapestry5.ioc.annotations.Inject; | |
import org.apache.tapestry5.ioc.annotations.Symbol; | |
import org.apache.tapestry5.services.BeanModelSource; | |
import es.com.blogspot.elblogdepicodev.tapestry.jpa.dao.ProductoDAO; | |
import es.com.blogspot.elblogdepicodev.tapestry.jpa.entities.Producto; | |
public class EjemploJPA { | |
@Inject | |
@Symbol(SymbolConstants.TAPESTRY_VERSION) | |
@Property | |
private String tapestryVersion; | |
@Inject | |
private ProductoDAO dao; | |
@Inject | |
private BeanModelSource beanModelSource; | |
@Inject | |
private ComponentResources resources; | |
@Property | |
private Producto producto; | |
public BeanModel<Producto> getModel() { | |
BeanModel<Producto> model = beanModelSource.createDisplayModel(Producto.class, resources.getMessages()); | |
// Columnas sin ordenación | |
for (String name :model.getPropertyNames()) { | |
model.get(name).sortable(false); | |
} | |
// Ocultar columna id | |
model.exclude("id"); | |
// Añadir columna de acción | |
model.add("action", null).label("").sortable(false); | |
return model; | |
} | |
void onNuevo() { | |
Producto producto = new Producto(UUID.randomUUID().toString(), UUID.randomUUID().toString(), 1l, new Date()); | |
dao.persist(producto); | |
} | |
void onEliminar(Long id) { | |
Producto producto = dao.findById(id); | |
dao.remove(producto); | |
} | |
@Cached | |
public List<Producto> getProductos() { | |
return dao.findAll(null); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html t:type="layout" | |
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd" | |
xmlns:p="tapestry:parameter"> | |
Versión: <b>${tapestryVersion}</b><br/> | |
<t:holaMundo/><br/> | |
<h1>Lista de productos</h1> | |
<t:grid source="productos" row="producto" model="model" lean="true" inPlace="true" class="table table-bordered table-condensed"> | |
<p:nombreCell> | |
${producto.nombre} | |
</p:nombreCell> | |
<p:actionCell> | |
<t:eventlink event="eliminar" context="producto.id" class="btn btn-danger">Eliminar</t:eventlink> | |
</p:actionCell> | |
<p:empty> | |
<p>No hay productos.</p> | |
</p:empty> | |
</t:grid> | |
<t:eventlink event="nuevo" class="btn btn-primary">Nuevo producto</t:eventlink> | |
</html> |
Para casos más complejos de transacciones como transacciones anidadas, requeridas, soportables, ninguna u obligatoria podemos basarnos en lo comentado en esta entrada donde queda perfectamente explicado.
Como en el resto de entradas el código fuente completo lo puedes encontrar en mi repositorio de GitHub. Si quieres probarlo en tu equipo lo puedes hacer de forma muy sencilla con los siguientes comandos y sin instalar nada previamente (salvo java y git). Si no dispones de git para clonar mi repositorio de GitHub puedes obtener el código fuente del repositorio en un archivo zip con el anterior enlace.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ git clone git://github.com/picodotdev/elblogdepicodev.git | |
$ cd elblogdepicodev/TapestryJPA | |
$ ./gradlew tomcatRun | |
# Abrir en el navegador http://localhost:8080/TapestrySecurity/ejemplojpa |
DAO genérico para JPA (ORM)
Documentación sobre Apache Tapestry
http://es.wikipedia.org/wiki/Java_Database_Connectivity
https://en.wikipedia.org/wiki/Relational_database
http://en.wikipedia.org/wiki/Cardinality_(data_modeling) http://en.wikipedia.org/wiki/ACIDhttp://en.wikipedia.org/wiki/SQL http://en.wikipedia.org/wiki/NoSQLhttp://en.wikipedia.org/wiki/Hibernate_(Java)