1. Introducción
1.1. Objectifs
El PDF del documento está disponible |AQUÍ|.
Los ejemplos del documento están disponibles |AQUÍ|.
El objetivo de este documento es explorar los conceptos principales de la persistencia de datos con API JPA (Java Persistence API). Tras leer este documento y probar los ejemplos, el lector debería haber adquirido los fundamentos necesarios para seguir adelante por su cuenta.
La API API JPA es reciente. Solo está disponible a partir de la versión 1.5 de JDK. La capa JPA tiene su lugar en una arquitectura de múltiples capas. Consideremos una arquitectura de este tipo bastante común, la de tres capas:
![]() |
- la capa [1], denominada aquí [ui] (Interfaz de usuario), es la capa que interactúa con el usuario, ya sea a través de una interfaz gráfica Swing, una interfaz de consola o una interfaz web. Su función es proporcionar datos del usuario a la capa [2] o bien presentar al usuario los datos proporcionados por la capa [2].
- La capa [2], denominada aquí [metier], es la capa que aplica las reglas denominadas de negocio, c.a.d. la lógica específica de la aplicación, sin preocuparse por el origen de los datos que se le proporcionan ni por el destino de los resultados que genera.
- La capa [3], denominada aquí [dao] (Objeto de Acceso a Datos), es la capa que proporciona a la capa [2] datos pre-registrados (archivos, bases de datos, ...) y que almacena algunos de los resultados proporcionados por la capa [2].
- La capa [JDBC] es la capa estándar utilizada en Java para acceder a bases de datos. Es lo que habitualmente se conoce como el controlador JDBC de SGBD.
Se han realizado múltiples esfuerzos para facilitar a los desarrolladores la escritura de estas diferentes capas. Entre ellos, JPA tiene como objetivo facilitar la creación de la capa [dao], la que administra los datos denominados «persistentes», de ahí el nombre de API (Java Persistence API). Una solución que ha ganado terreno en los últimos años en este ámbito es la de Hibernate:
![]() |
La capa [Hibernate] se sitúa entre la capa [dao], escrita por el desarrollador, y la capa [Jdbc]. Hibernate es un ORM (mapeo objeto-relacional), una herramienta que sirve de puente entre el mundo relacional de las bases de datos y el de los objetos manipulados por Java. El desarrollador de la capa [dao] ya no ve la capa [Jdbc] ni las tablas de la base de datos cuyo contenido desea utilizar. Solo ve la representación objetiva de la base de datos, proporcionada por la capa [Hibernate]. El enlace entre las tablas de la base de datos y los objetos manipulados por la capa [dao] se establece principalmente de dos maneras:
- mediante archivos de configuración del tipo XML
- mediante anotaciones Java en el código, técnica disponible solo a partir de la versión 1.5 de JDK
La capa [Hibernate] es una capa de abstracción que busca ser lo más transparente posible. El objetivo ideal es que el desarrollador de la capa [dao] pueda ignorar por completo que está trabajando con una base de datos. Esto es posible si no es él quien escribe la configuración que sirve de puente entre el mundo relacional y el mundo de objetos. La configuración de este puente es bastante delicada y requiere cierta práctica.
La capa de objetos [4], que es un reflejo de la BD, se denomina «contexto de persistencia». Una capa [dao] basada en Hibernate realiza acciones de persistencia (CRUD: crear, leer, actualizar, eliminar) sobre los objetos del contexto de persistencia; estas acciones son traducidas por Hibernate en órdenes SQL. Para las acciones de consulta a la base de datos (el SQL Select), Hibernate proporciona al desarrollador un lenguaje HQL (Hibernate Query Language) para consultar el contexto de persistencia [4] y no la base de datos BD en sí misma.
Hibernate es popular, pero complejo de dominar. La curva de aprendizaje, que a menudo se presenta como fácil, es en realidad bastante empinada. Tan pronto como se tiene una base de datos con tablas que tienen relaciones uno a varios o varios a varios, la configuración del puente relacional/objetos no está al alcance de cualquier principiante. Los errores de configuración pueden entonces dar lugar a aplicaciones de bajo rendimiento.
En el ámbito comercial, existía un producto equivalente a Hibernate llamado Toplink:
![]() |
Ante el éxito de los productos ORM, Sun, el creador de Java, decidió estandarizar una capa ORM mediante una especificación llamada JPA, que apareció al mismo tiempo que Java 5. La especificación JPA fue implementada por los dos productos Toplink e Hibernate. Toplink, que era un producto comercial, se ha convertido desde entonces en un producto libre. Con JPA, la arquitectura anterior queda así:
![]() |
La capa [dao] ahora interactúa con la especificación JPA, un conjunto de interfaces. El desarrollador ha ganado en estandarización. Antes, si cambiaba su capa ORM, también tenía que cambiar su capa [dao], que se había escrito para interactuar con un ORM específico. Ahora, escribirá una capa [dao] que se comunicará con una capa JPA. Independientemente del producto que la implemente, la interfaz de la capa JPA que se presenta a la capa [dao] sigue siendo la misma.
Este documento presentará ejemplos de JPA en diversos ámbitos:
- en primer lugar, nos centraremos en el puente relacional/objeto que construye la capa ORM. Este se creará mediante anotaciones de Java 5 para bases de datos en las que existan relaciones entre tablas de tipo:
- uno a uno
- uno a varios
- varios a varios
Para ilustrar este ámbito, crearemos las siguientes arquitecturas de prueba:
![]() |
Nuestros programas de prueba serán aplicaciones de consola que consultarán directamente la capa JPA. En esta ocasión, conoceremos los principales métodos de la capa JPA. Estaremos en un entorno denominado «Java SE» (Edición Estándar). JPA funciona tanto en un entorno Java SE como en uno Java EE5 (Edición Empresarial).
- Cuando dominemos tanto la configuración del puente relacional/objeto como el uso de los métodos de la capa JPA, volveremos a una arquitectura multicapa más clásica:
![]() |
Se accederá a la capa [JPA] a través de una arquitectura de dos capas: [metier] y [dao]. Se utilizará el marco Spring [7], y luego el contenedor EJB3 de JBoss para vincular estas capas entre sí.
Como mencionamos anteriormente, JPA está disponible en los entornos SE y EE5. El entorno Java EE5 ofrece numerosos servicios en el ámbito del acceso a datos persistentes, en particular grupos de conexiones, administradores de transacciones, etc. Puede ser interesante para un desarrollador aprovechar estos servicios. El entorno Java EE5 aún no está muy extendido (mayo de 2007). Actualmente se encuentra en el servidor de aplicaciones Sun Application Server 9.x (Glassfish). Un servidor de aplicaciones es, esencialmente, un servidor de aplicaciones web. Si se desarrolla una aplicación gráfica independiente de tipo Swing, no se puede disponer del entorno EE ni de los servicios que ofrece. Esto representa un problema. Se están empezando a ver entornos EE «independientes», c.a.d. que pueden utilizarse fuera de un servidor de aplicaciones. Este es el caso de JBos y EJB3, que utilizaremos en este documento.
En un entorno EE5, las capas se implementan mediante objetos llamados EJB (Enterprise Java Bean). En versiones anteriores de EE, los EJB (EJB y 2.x) tenían fama de ser difíciles de implementar y de probar, y en ocasiones ofrecían un rendimiento deficiente. Se distingue entre los EJB2.x «entity» y los EJB2.x «session». En resumen, un EJB2.x «entidad» es la representación de una fila de una tabla de base de datos y un EJB2.x «sesión» es un objeto utilizado para implementar las capas [metier], [dao] de una arquitectura multicapa. Una de las principales críticas a las capas implementadas con EJB es que solo se pueden utilizar dentro de contenedores EJB, un servicio proporcionado por el entorno EE. Esto complica las pruebas unitarias. Así, en el esquema anterior, las pruebas unitarias de las capas [metier] y [dao], construidas con EJB, requerirían la configuración de un servidor de aplicaciones, una operación bastante engorrosa que no motiva realmente al desarrollador a realizar pruebas con frecuencia.
El marco Spring surgió como respuesta a la complejidad de los EJB2. Spring proporciona, en un entorno SE, una gran cantidad de los servicios que suelen ofrecer los entornos EE. Así, en la sección de «Persistencia de datos» que nos interesa aquí, Spring proporciona los grupos de conexiones y los administradores de transacciones que necesitan las aplicaciones. La aparición de Spring ha fomentado la cultura de las pruebas unitarias, que de repente se volvieron mucho más fáciles de implementar. Spring permite implementar las capas de una aplicación mediante objetos Java clásicos (POJO, Plain Old/Ordinary Java Object), lo que permite reutilizarlos en otro contexto. Por último, integra numerosas herramientas de terceros de manera bastante transparente, en particular herramientas de persistencia como Hibernate, iBatis, etc.
Java EE5 fue diseñado para corregir las deficiencias de la especificación anterior EE. Los EJB y 2.x se han convertido en los EJB3. Estos son POJOs etiquetados con anotaciones que los convierten en objetos especiales cuando se encuentran dentro de un contenedor EJB3. Dentro de este, el EJB3 podrá beneficiarse de los servicios del contenedor (pool de conexiones, administrador de transacciones, etc.). Fuera del contenedor EJB3, el EJB3 se convierte en un objeto Java normal. Sus anotaciones EJB se ignoran.
Anteriormente, hemos representado a Spring y a JBoss EJB3 como una posible infraestructura (framework) de nuestra arquitectura multicapa. Es esta infraestructura la que proporcionará los servicios que necesitamos: un grupo de conexiones y un administrador de transacciones.
- Con Spring, las capas se implementarán mediante POJOs. Estos tendrán acceso a los servicios de Spring (pool de conexiones, administrador de transacciones) mediante la inyección de dependencias en estos POJOs: al crearlos, Spring les inyecta referencias a los servicios que van a necesitar.
-
JBoss EJB3 es un contenedor EJB que puede funcionar fuera de un servidor de aplicaciones. Su principio de funcionamiento (para el desarrollador) es análogo al descrito para Spring. Encontraremos pocas diferencias.
-
Terminaremos el documento con un ejemplo de aplicación web de tres capas, básica pero representativa:
![]() |
1.2. Références
[ref1]: Java Persistence with Hibernate, de Christian Bauer y Gavin King, editorial Manning.
[ref1] es el documento que sirvió de base para lo que sigue. Se trata de un libro exhaustivo de más de 800 páginas sobre el uso de Hibernate en dos contextos diferentes: con o sin JPA. El uso de Hibernate sin JPA sigue siendo, de hecho, relevante para los desarrolladores que utilizan JDK 1.4 o versiones anteriores, ya que JPA no apareció hasta la versión 1.5 de JDK.
Después de leer más de tres cuartas partes del libro y hojear el resto, me pareció que todo en este documento era útil. El usuario experimentado de Hibernate debería conocer casi toda la información que se ofrece en las 800 páginas. Christian Bauer y Gavin King han sido exhaustivos, pero rara vez para describir situaciones con las que nunca nos encontraremos. Vale la pena leerlo todo. El libro está escrito de manera didáctica: hay un verdadero interés por no dejar nada en la oscuridad. El hecho de que haya sido escrito para el uso de Hibernate tanto con como sin JPA supone una dificultad para quienes solo están interesados en una u otra de estas tecnologías. Por ejemplo, los autores describen, a través de numerosos ejemplos, el puente relacional/objeto en ambos contextos. Los conceptos utilizados son muy similares, ya que JPA se inspiró en gran medida en Hibernate. Sin embargo, presentan algunas diferencias. De tal manera que algo que es cierto para Hibernate puede no serlo para JPA, lo que termina generando confusión en el lector.
Los autores muestran ejemplos de aplicaciones de tres capas en el contexto de un contenedor EJB3. No mencionan a Spring. Veremos en un ejemplo que Spring es, sin embargo, más sencillo de usar y tiene un enfoque más integral que el contenedor JBoss EJB3 utilizado en [ref1]. No obstante, «Java Persistence with Hibernate» es un excelente libro que recomiendo por todos los conceptos básicos que enseña sobre los ORM.
Usar un ORM es complicado para un principiante.
- Hay conceptos que hay que entender para configurar el puente relacional/objeto.
- Está el concepto de contexto de persistencia con sus nociones de objetos en estado «persistente», «desvinculado» y «nuevo»
- está la mecánica en torno a la persistencia (transacciones, grupos de conexiones), que por lo general son servicios proporcionados por un contenedor
- Hay ajustes que se deben realizar para optimizar el rendimiento (caché de segundo nivel).
- ...
Presentaremos estos conceptos mediante ejemplos. No nos extenderemos mucho en la teoría al respecto. Nuestro objetivo es simplemente, en cada caso, permitir que el lector comprenda el ejemplo y lo asimile hasta el punto de poder realizar modificaciones por sí mismo o aplicarlo en otro contexto.
1.3. Herramientas utilizadas
Los ejemplos de este documento utilizan las siguientes herramientas. Algunas se describen en los anexos (descarga, instalación, configuración, uso). En ese caso, se indica el número de párrafo y la página.
- un JDK 1.6 (párrafo 5.1)
- el IDE para el desarrollo en Java con Eclipse 3.2.2 (párrafo 5.2)
- el complemento de Eclipse WTP (Web Tools Package) (párrafo 5.2.3)
- Complemento de Eclipse SQL Explorer (párrafo 5.2.6)
- Complemento de Eclipse Hibernate Tools (párrafo 5.2.5)
- complemento de Eclipse TestNG (párrafo 5.2.4)
- Contenedor de servlets Tomcat 5.5.23 (párrafo 5.3)
- SGBD Firebird 2.1 (párrafo 5.4)
- SGBD MySQL5 (párrafo 5.5)
- SGBD PosgreSQL (párrafo 5.6)
- SGBD Oracle 10g Express (párrafo 5.7)
- SGBD SQL Server 2005 Express (párrafo 5.8)
- SGBD HSQLDB (párrafo 5.9)
- SGBD Apache Derby (párrafo 5.10)
- Spring 2.1 (párrafo 5.11)
- Contenedor EJB3 de JBoss (párrafo 5.12)
1.4. Descarga de los ejemplos de
En el sitio web de este documento, los ejemplos analizados se pueden descargar en forma de archivo zip, que una vez descomprimido, genera la siguiente carpeta:
![]() |
- en [1]: la estructura de los ejemplos
- en [2]: la carpeta <annexes> contiene los elementos presentados en la sección ANNEXES, párrafo 5. En particular, la carpeta <jdbc> contiene los controladores JDBC de SGBD utilizados para los ejemplos del tutorial.
- en [3]: la carpeta <lib> agrupa en 5 carpetas los distintos archivos .jar utilizados por el tutorial
- en [4]: la carpeta <lib/divers> agrupa los archivos: - los controladores JDBC de SGBD - de la herramienta de pruebas unitarias [testNG] - de la herramienta de registros [log4j]
![]() |
- en [5]: los archivos de la implementación JPA/Hibernate y de las herramientas de terceros necesarias para Hibernate
- en [6]: los archivos de la implementación JPA/Toplink
- en [7]: los archivos de Spring 2.x y de las herramientas de terceros necesarias para Spring
- en [8]: los archivos del contenedor EJB3 de JBoss
![]() |
- en [9]: la carpeta <hibernate> contiene los ejemplos procesados con la capa de persistencia JPA/Hibernate
- en [10]: la carpeta <hibernate/direct> reúne los ejemplos en los que la capa JPA se utiliza directamente con un programa de tipo [Main].
- en [11] y [12]: ejemplos en los que la capa JPA se utiliza a través de las capas [metier] y [dao] en una arquitectura multicapa, lo cual constituye el caso de uso habitual. Los servicios (grupo de conexiones, administrador de transacciones) que utilizan las capas [metier] y [dao] son proporcionados ya sea por Spring [11] o por JBoss, EJB3 y [12].
![]() |
- en [13]: la carpeta <toplink> retoma los ejemplos de la carpeta <hibernate> [9], pero esta vez con una capa de persistencia JPA/Toplink en lugar de JPA/Hibernate. NoEn [13] no hay una carpeta <jbossejb3>, ya que no fue posible hacer funcionar un ejemplo en el que la capa de persistencia estuviera a cargo de Toplink y los servicios, a cargo del contenedor EJB3 de JBoss.
- En [14]: una carpeta <web> reúne tres ejemplos de aplicaciones web con una capa de persistencia JPA:
- [15]: un ejemplo con Spring / JPA / Hibernate
- [16]: el mismo ejemplo con Spring / JPA / Toplink
- [17]: el mismo ejemplo con JBoss EJB3 / JPA / Hibernate. Este ejemplo no funciona, probablemente debido a un problema de configuración sin resolver. No obstante, se ha dejado para que el lector pueda analizarlo y, tal vez, encontrar una solución a este problema.
El tutorial hace referencia con frecuencia a esta estructura de directorios, especialmente al probar los ejemplos estudiados. Se invita al lector a descargar estos ejemplos e instalarlos. De aquí en adelante, nos referiremos a la estructura de directorios de los ejemplos descrita anteriormente como <ejemplos>.
1.5. Configuración de los proyectos « » de Eclipse de los ejemplos
Los ejemplos utilizan bibliotecas «de usuario». Se trata de archivos .jar agrupados bajo un mismo nombre. Cuando se incluye una biblioteca de este tipo en el classpath de un proyecto Java, todos los archivos que contiene se incluyen en esa ruta de clases. Veamos cómo hacerlo en Eclipse:
![]() |
- en [1]: [Window / Preferences / Java / Buld Path / User Libraries]
- en [2]: creamos una nueva biblioteca
- en [3]: le damos un nombre y lo validamos
![]() |
- en [4]: seleccionamos los archivos JAR que formarán parte de la biblioteca [jpa-divers]
- en [5]: seleccionamos todos los archivos JAR de la carpeta <ejemplos>/lib/divers
![]() |
- en [6]: se ha definido la biblioteca de usuario [jpa-divers]
- en [7]: se repite el mismo procedimiento para crear otras 4 bibliotecas:
Biblioteca | Carpeta de los archivos JAR de la biblioteca |
<ejemplos>/lib/hibernate | |
<ejemplos>/lib/toplink | |
<ejemplos>/lib/spring | |
<ejemplos>/lib/jbossejb3 |













