Comienzo rápido con Tomcat Empezando con Hibernate Este tutorial explica una instalación de Hibernate con el contenedor de servlets Apache Tomcat (hemos usado la versión 4.1, las diferencias con la 5.0 deben ser mínimas) para una aplicación basada en web. Hibernate trabaja bien en un entorno manejado con todos los servidores de aplicaciones J2EE importantes, o incluso en aplicaciones Java independientes. El sistema de base de datos es sólo una cuestión de cambiar la configuración del dialecto SQL de Hibernate y las propiedades de conexión. Primero, tenemos que copiar todas las bibliotecas requeridas a la instalación de Tomcat. Usamos un contexto web separado (webapps/quickstart) para este tutorial, de modo que tenemos que considerar tanto la ruta de búsqueda de bibliotecas global (TOMCAT/common/lib) como también el cargador de clases a nivel de contexto en webapps/quickstart/WEB-INF/lib (para ficheros JAR) y webapps/quickstart/WEB-INF/classes. Nos referiremos a ambos niveles de cargador de clases como el classpath global y el classpath de contexto, respectivamente. Ahora, copia las bibliotecas a los dos classpaths: Copia el driver JDBC para la base de datos al classpath global. Esto se requiere para el software de pool de conexiones DBCP que se distribuye con Tomcat. Hibernate usa conexiones JDBC para ejecutar SQL sobre la base de datos, de modo que, o bien tienes que proveer conexiones JDBC en pool, o bien configurar Hibernate para que use uno de los pools soportados directamente (C3P0, Proxool). Para este tutorial, copia la biblioteca pg74jdbc3.jar (para PostgreSQL 7.4 y JDK 1.4) al classpath del cargador global. Si quisieras usar una base de datos diferente, simplemente copia su apropiado driver JDBC. Nunca copies nada más dentro de la ruta del cargador de clases global en Tomcat, o tendrás problemas con varias herramientas, incluyendo Log4J, commons-logging y otras. Siempre usa el classpath de contexto para cada aplicación web, esto es, copia las bibliotecas a WEB-INF/lib y tus propias clases y ficheros de configuración/propiedades a WEB-INF/classes. Ambos directorios están a nivel del classpath de contexto por defecto. Hibernate está empaquetado como una biblioteca JAR. El fichero hibernate3.jar debe ser copiado en el classpath de contexto junto a las otras clases de la aplicación. Hibernate requiere algunas bibliotecas de terceros en tiempo de ejecución; éstas vienen incluídas con la distribución de Hibernate en el directorio lib/. Ver . Copia las bibliotecas de terceros requeridas al classpath de contexto. Bibliotecas de terceros de Hibernate Biblioteca Descripción antlr (requerida) Hibernate usa ANTLR para producir analizadores de consultas, esta biblioteca también se necesita en tiempo de ejecución. dom4j (requerida) Hibernate usa dom4j para analizar ficheros de configuración XML y ficheros de metadatos de mapeo XML. CGLIB, asm (requerida) Hibernate usa la biblioteca de generación de código para aumentar las clases en tiempo de ejecución (en combinación con reflección Java). Commons Collections, Commons Logging (requeridas) Hibernate usa varias bibliotecas de utilidad del proyecto Jakarta Commons de Apache. EHCache (requerida) Hibernate puede usar varios provedores de caché para el caché de segundo nivel. EHCache es el provedor de caché por defecto si no se cambia en la configuración. Log4j (opcional) Hibernate usa la API de Commons Logging, que a su vez puede usar Log4J como el mecanismo de logging subyacente. Si la biblioteca Log4J está disponible en el directorio de bibliotecas del contexto, Commons Logging usará Log4J y la configuración log4j.properties en el classpath de contexto. Un fichero de propiedades de ejemplo para Log4J se incluye con la distribución de Hibernate. Así que copia log4j.jar y el fichero de configuración (de src/) a tu classpath de contexto si quieres ver que ocurre tras escénas. ¿Requerida o no? Echa una mirada al fichero lib/README.txt en la distribución de Hibernate. Esta es una lista actualizada de bibliotecas de terceros distribuídas con Hibernate. Encontrarás listadas ahí todas las bibliotecas requeridas y opcionales (Observa que "buildtame required" significa aquí para la construcción de Hibernate, no de tu aplicación).
Ahora instalamos el pooling y modo compartido de conexiones de base de datos tanto en Tomcat como Hibernate. Esto significa que Tomcat proveerá conexiones JDBC en pool (usando su funcionalidad prefabricada de pooling DBCP). Hibernate pide esas conexiones a través de JNDI. Alternativamente, puedes dejar que Hibernate maneje el pool de conexiones. Tomcat liga su pool de conexiones a JNDI; agregamos una declaración de recurso al fichero de configuración principal de Tomcat, TOMCAT/conf/server.xml: factory org.apache.commons.dbcp.BasicDataSourceFactory url jdbc:postgresql://localhost/quickstart driverClassNameorg.postgresql.Driver username quickstart password secret maxWait 3000 maxIdle 100 maxActive 10 ]]> El contexto que configuramos en este ejemplo se llama quickstart, su base es el directorio TOMCAT/webapp/quickstart. Para acceder a cualquier servlet, llama a la ruta http://localhost:8080/quickstart en tu navegador (por supuesto, agregando el nombre del servlet como se mapee en tu web.xml). Puedes también ir más allá y crear ahora un servlet simple que tenga un método process() vacío. Tomcat provee ahora conexiones a través de JNDI en java:comp/env/jdbc/quickstart. Si tienes problemas obteniendo el pool de conexiones en ejecución, refiérete a la documentación de Tomcat. Si obtienes mensajes de excepción del driver JDBC, intenta instalar primero el pool de conexiones JDBC sin Hibernate. Hay disponibles en la Web tutoriales de Tomcat y JDBC. Tu próximo paso es configurar Hibernate. Hibernate tiene que saber cómo debe obtener conexiones JDBC. Usamos la configuración de Hibernate basada en XML. El otro enfoque, usando un ficheros de propiedad, es casi equivalente pero pierde unas pocas funcionalidades que sí permite la sintaxis XML. El fichero de configuración XML se ubica en el classpath de contexto (WEB-INF/classes), como hibernate.cfg.xml: java:comp/env/jdbc/quickstart false org.hibernate.dialect.PostgreSQLDialect ]]> Desactivamos el registro (logging) de comandos SQL y decimos a Hibernate qué dialecto SQL de base de datos se usa y dónde obtener conexiones JDBC (declarando la dirección JNDI del pool ligado a Tomcat). El dialecto es una configuración requerida, las bases de datos difieren en su interpretación del "estándar" de SQL. Hibernate cuidará de las diferencias y viene con dialectos incluídos para todas las principales bases de datos comerciales y de código abierto. Una SessionFactory es el concepto de Hibernate de un almacén de datos solo. Pueden usarse múltiples bases de datos creando múltiples ficheros de configuración XML y creando múltiples objetos Configuration y SessionFactory en tu aplicación. El último elemento del hibernate.cfg.xml declara Cat.hbm.xml como el nombre de un fichero de mapeo XML para la clase persistente Cat. Este fichero contiene los metadatos para el mapeo de la clase POJO Cat a una tabla (o tablas) de base de datos. Volveremos a este fichero pronto. Escribamos primero la clase POJO y luego declaremos los metadatos de mapeo para ella.
Primera clase persistente Hibernate trabaja mejor con el modelo de programación de los Viejos Objetos Planos de Java (POJOs, a veces llamados Ordinarios Objetos Planos de Java) para clases persistentes. Un POJO es como un JavaBean, con las propiedades de la clase accesible vía métodos getter y setter, encapsulando la representación interna de la interfaz publicamente visible (Hibernate puede también acceder a los campos directamente, si se necesita): Hibernate no está restringido en su uso de tipos de propiedad, todos los tipos y tipos primitivos del JDK de Java (como String, char y Date) pueden ser mapeados, incluyendo clases del framework de colecciones de Java. Puedes mapearlos como valores, colecciones de valores, o asociaciones a otras entidades. El id es una propiedad especial que representa el identificador de base de datos (clave primaria) de la clase. Es altamente recomendado para entidades como un Cat. Hibernate puede usar identificadores sólo internamente, pero perderíamos algo de la flexibilidad en nuestra arquitectura de aplicación. No tiene que implementarse ninguna interface especial para las clases persistentes ni tienes que subclasear de una clase persistente raíz en especial. Hibernate tampoco requiere ningún procesamiento en tiempo de construcción, como manipulación del byte-code. Se basa solamente en reflección de Java y aumentación de clases en tiempo de ejecución (a través de CGLIB). De modo que, sin ninguna dependencia de la clase POJO en Hibernate, podemos mapearla a una tabla de base de datos. Mapeando el gato El fichero de mapeo Cat.hbm.xml contiene los metadatos requeridos para el mapeo objeto/relacional. Los metadatos incluyen la declaración de clases persistentes y el mapeo de propiedades (a columnas y relaciones de claves foráneas a otras entidades) a tablas de base de datos. ]]> Cada clase persistente debe tener un atributo identificador (realmente, sólo las clases que representen entidades, no las clases dependientes de tipo-valor, que son mapeadas como componentes de una entidad). Esta propiedad es usada para distinguir los objetos persistentes: Dos gatos son iguales si catA.getId().equals(catB.getId()) es verdadero. Este concepto se llama identidad de base de datos (database identity). Hibernate viene empaquetado con varios generadores de identificador para diferentes escenarios (incluyendo generadores nativos para secuencias de base de datos, tablas de identificadores alto/bajo, e identificadores asignados por aplicación). Usamos el generador UUID (recomendado sólo para pruebas, pues deben preferirse las claves enteras delegadas generadas por la base de datos) y también especificamos la columna CAT_ID de la tabla CAT para el valor identificador generado por Hibernate (como una clave primaria de la tabla). Todas las demás propiedades de Cat son mapeadas a la misma tabla. En el caso de la propiedad name, la hemos mapeado con una declaración explícita de columna de base de datos. Esto es especialmente útil cuando el esquema de base de datos es generado automáticamente (como sentencias DDL de SQL) desde la declaración de mapeo con la herramienta SchemaExport de Hibernate. Todas las demás propiedades son mapeadas usando la configuración por defecto de Hibernate, que es lo que necesitas la mayoría del tiempo. La tabla CAT en la base de datos se ve así como: Ahora debes crear esta tabla manualmente en tu base de datos, y luego leer el si quieres automatizar este paso con la herramienta hbm2ddl. Esta herramienta puede crear un DDL SQL completo, incluyendo definición de tablas, restricciones personalizadas de tipo de columnas, restricciones de unicidad e índices. Jugando con gatos Ahora estamos listos para comenzar la Session de Hibernate. Es el manejador de persistencia que usamos para almacenar y traer Cats hacia y desde la base de datos. Pero primero, tenemos que obtener una Session (unidad de trabajo de Hibernate) de la SessionFactory: La llamada a configure() carga el fichero de configuración hibernate.cfg.xml e inicializa la instancia de Configuration. Puedes establecer otras propiedades (e incluso cambiar los metadatos de mapeo) accediendo a la Configuration antes que construyas la SessionFactory (que es inmutable). ¿Dónde creamos la SessionFactory y cómo accedemos a ella en nuestra aplicación? Una SessionFactory usualmente se construye una vez, por ejemplo, al arrancar con un servlet load-on-startup. Esto significa también que no debes mantenerla en una variable de instancia en tus servlets, sino en alguna otro sitio. Además, necesitamos algún tipo de Singleton, de modo que podamos acceder a la SessionFactory fácilmente en el código de aplicación. El siguiente enfoque mostrado resuelve ambos problemas: configuración de arranque y fácil acceso a una SessionFactory. Implementamos una clase de ayuda HibernateUtil: Esta clase no sólo cuida de la SessionFactory con su inicializador static, sino que además tiene una variable ThreadLocal que tiene la Session para la hebra actual. Asegúrate de entender el concepto Java de una variable local a una hebra antes de intentar usar esta ayuda. Una clase HibernateUtil más compleja y potente puede encontrarse en CaveatEmptor, http://caveatemptor.hibernate.org/ Una SessionFactory es segura entre hebras, muchas hebras pueden acceder a ella concurrentemente y pedirle Sessions. Una Session no es un objeto seguro entre hebras que representa una sola unidad-de-trabajo con la base de datos. Las Sessions se abren desde una SessionFactory y son cerradas cuando todo el trabajo está completo. Un ejemplo en el método process() de tu servlet podría parecerse a esto (sin manejo de excepciones): En una Session, cada operación de base de datos ocurre dentro de una transacción que aísla las operaciones de base de datos (incluso operaciones de sólo lectura). Usamos la API de Transaction de Hibernate para abstraer de la estrategia de transacciones subyacente (en nuestro caso, transacciones JDBC). Esto permite que nuestro código sea desplegado con transacciones manejadas por contenedor (usando JTA) sin cambio alguno. Observa que puedes llamar HibernateUtil.currentSession(); tantas veces como quieras, siempre obtendrás la Session actual de esta hebra. Tienes que asegurarte que la Session sea cerrada después que se complete tu unidad-de-trabajo, ya sea en código de tu servlet o en un filtro de servlet antes que la respuesta HTTP sea enviada. El bonito efecto colateral de la segunda opción es la fácil inicialización perezosa: la Session todavía está abierta cuando se dibuja la vista, de modo que Hibernate puede cargar objetos no inicializados mientras navegas tu actual grafo de objetos. Hibernate tiene varios métodos que pueden ser usados para traer objetos desde la base de datos. La forma más flexible es usando el Lenguaje de Consulta de Hibernate (Hibernate Query Language o HQL), que es una extensión orientada a objetos de SQL fácil de aprender: Hibernate también ofrece una API consulta por criterios orientada a objetos que puede ser usada para formular consultas de tipo seguro. Por supuesto, Hibernate usa PreparedStatements y ligado de parámetros para toda la comunicación SQL con la base de datos. También puedes usar la funcionalidad de consulta SQL directa de Hibernate u obtener una conexión plana de JDBC de una Session en casos raros. Finalmente Rasguñamos solamente la superficie de Hibernate en este pequeño tutorial. Por favor, observa que no incluimos ningún código específico de servlet en nuestros ejemplos. Tienes que crear un servlet por tí mismo e insertar el código de Hibernate como lo veas ubicado. Ten en mente que Hibernate, como capa de acceso a datos, está firmemente integrado dentro de tu aplicación. Usualmente, todas las otras capas dependen del mecanismo de persistencia. Asegúrate de entender las implicaciones de este diseño. Para un ejemplo de aplicación más compleja, ver http://caveatemptor.hibernate.org/ y echa una mirada a los otros tutoriales con links en http://www.hibernate.org/Documentation