La primera versión beta de Talend 4 (software comercial para ETL) tiene un pequeño error que impide que se ejecute a la primera en OS X.
Para una tarea de inteligencia de negocios nos pidieron usar Talend para el proceso de ETL y cuando fui a ejecutar el TalendOpenStudio-macosx-carbon.app de la versión de prueba de una temprana versión beta de Talend 4.0.0, aparecía el siguiente error:
Para arreglarlo había que abrir el archivo TOS-All-r33789-V4.0.0M1/TalendOpenStudio-macosx-carbon.app/Contents/MacOS/TalendOpenStudio-macosx-carbon.ini. (Para abrir la .app, en el menú contextual sobre el archivo de la aplicación seleccionar Mostrar contenido del paquete).
Con eso, pude ejecutar la versión 4 (que ya viene con los permisos de ejecutables resueltos) con sólo hacer click en TalendOpenStudio-macosx-carbon.app en el dock.
Estaba tratando de conectarme a Oracle desde SQL Developer pero el formulario de la conexión me pedía el SID. ¿Pero cuál es mi SID? ¿Ah?
Por suerte, Oracle estaba instalado sobre un ambiente parecido a UNIX como lo es Linux y pude conocer este dato entrando por SSH al servidor y escribiendo el comando:
echo $ORACLE_SID
Para administrar mi base de datos estoy usando el software gratuito oficial de Oracle multi-plataforma SQL Developer (requiere registro previo en oracle.com).
MySQL tiene la posibilidad de crear columnas que se auto-incrementan en la misma definición de la columna a travé del comando AUTO_INCREMENT, no así PostgreSQL. En Postgres es más complicado, y de esta manera lo hago yo.
1. Crear secuencia
Con pgAdmin III
pgAdmin3 es una aplicación multi-plataforma que sirve para administrar las bases de datos PostgreSQL hecha por la misma comunidad PostgreSQL. Si l quieres hacer por líneas de comandos, avanza un poco más en este artículo.
Una vez abierto el pgAdmnin3 y hecha la conexión:
Expando mi base datos, mi Esquema, elijo con el botón secundario del mouse la sección Secuencias y elijo en el menú contextual la opción Nueva Secuencia...
En el formulario le pongo un nombre (en mi caso se llamará transferencias_id), el usuario que hará uso de esta secuencia (en mi caso, el usurio se llama ooscarr) y OK. También puedo elegir si quier que se auto-incremente a partir de un número diferente al 1 y/o si quiero que el número vaya aumentando de 2 en 2, 3 en 3, etc. en vez de 1 en 1 como se pone por defecto.
Lo mismo, desde la línea de comandos
si seleccionas la nueva secuencia que se agrega a la lista puedes ver el comando SQL que se utilizó para crearla. Lo mismo se pudo haber hecho desde la línea de comandos psql con:
CREATE SEQUENCE transferencias_id;
ó
CREATE SEQUENCE transferencias_id
INCREMENT 1
MINVALUE 1
START 1
CACHE 1;
ALTER TABLE transferencias_id OWNER TO ooscarr;
transferencias_id es el nombre que le asigné a la secuencia y ooscarr es el usuario de PostgreSQL que es dueño de esta variable auto-incrementable.
2. Usar la secuencia en una columna
Con pgAdmin3
Expandemos la tabla que queremos modificar, expandemos Columnas, y seleccionamos la columna de la tabla que se deseas que se auto-incremente con esta variable, vas a las Propiedades....
En el inspector de columna, le asignamos el Valor por defectoNEXTVAL('transferencias_id'). Siendo transferencias_id el nombre de la secuencia.
OK
Con comandos (psql)
Sería...
ALTER TABLE transferencias
ALTER COLUMN id
SET DEFAULT NEXTVAL('transferencias_id');
transferencias_id es el nombre de la secuencia, id es el nombre de la columna a asignarle la secuencia y transferencias es el nombre de mi tabla donde está la columna con al secuencia.
Actualizar filas anteriores sin la secuencia
Con comandos
Para cambiar los valores null de las filas creadas antes de la asignación de la variable auto-incrementable, consultamos:
UPDATE transferencias
SET id = NEXTVAL('transferencias_id');
Ese paso final es una de las cosas más lentas que puedes pedirle a Postgres que haga posiblemente. Para una tabla de tamaño mediano (alrededor de 5,000,000 tuplas, con unas cuantas columnas de pequeños números y textos), eso tomó cerca de 2.5 horas en hardware poderoso - así que querrás dejar esto por un tiempo tranquilo. Afortunadamente Postgres interpreta el UPDATE como una transacción atómica: nada se realiza hasta que el comando se complete, así será difícil que dejes los datos en un estado inconsistente.
Y ya con eso, cada vez que crees una nueva fila en esa tabla, esa columna se llenará automáticamente con un número distinto automáticamente.
Al final de este tutorial conocerás la dinámica del desarrollo de una aplicación web manejando la persistencia de la base de datos con Hibernate y cómo NetBeans IDE te ayuda a escribir el código y consultar la base de datos. También escribirás una aplicación web JSP que haga consultas a esta base de datos a través usando XML.
Este ejemplo de Hibernate es famoso, está documentado en varias páginas, pero todas tienen el código malo y es frustrante aprender así. Así que lo corregí y lo traduje al español sacando cosas de uno y de otro mismo tutorial que encontré en ¡chino, portugués, inglés y ruso!
Soporte para Hibernate en aplicaciones web está disponible desde NetBeans IDE 6.1. Desde NetBeans IDE 6.5, también puedes hacer ingeniería inversa a las tablas de una BD a archivos de mapeo y clases Java correspondientes usando un asistente intuitivo.
Pasos previos
Antes de empezar necesitarás:
Instalar NetBeans IDE. Al momento de escribir esto, anda dando vueltas la versión 6.8 RC2.
Instalar MySQL o Java DB, o tener acceso a conectarte a una de estas bases de datos con permiso para acceder o crear la base de datos Sakila. La base de datos Sakila está disponible en estos dos tipos de bases de datos, pero Hibernate funciona con cualquier JDBC, por ejemplo PostgreSQL y Oracle.
Una vez obtenido esto, asegúrate de configurar NetBeans IDE para poder acceder a la base de datos Sakila desde el panel "Prestaciones" (Menú Ventana > Prestaciones.
Instalar el complemento de Hibernate
El plug-in, si no lo tienes ya instalado, lo obtienes
yendo, en el menú, a Herramientas > Complementos.
En la ficha Plugins disponibles, selecciona la casilla del módulo Hibernate y presiona Instalar.
Siguiente
Acepta los términos de todos los contratos de licencia y presiona el botón de Instalar.
Después que se descargue y se instale, presiona Terminar.
Y listo, el módulo para Hibernate (o Hibernar) se activará solo; o sino, reinicia NetBeans IDE.
Crear Proyecto nuevo
Primero: lo típico. Creamos un Proyecto nuevo de tipo Aplicación web.
Para nuestro ejemplo, le damos el nombre DVDStore.
Elegimos un servidor como Tomcat o Glassfish, Siguiente y marcamos la opción Hibernate y seleccionamos la base de datos jdbc:mysql://localhost:3306/sakila, en nuestro caso, de la lista que, supongo, ya habías configurado anteriormente como indiqué en los pasos previos de este tutorial.
Terminar
Fíjate que en:
DVDStore
Libraries
se agregaron automáticamente las librerías necesarias para utilizar Hibernate y conectar a la base de datos.
Configurar proyecto
hibernate.cfg.xml
Abre el archivo hibernate.cfg.xml que está en la ruta:
DVDStore
Source Packages
<paquete predeterminado>
hibernate.cfg.xml
Una vez abierto en NetBeans IDE, en el modo Diseño, en la parte Propiedades opcionales, Propiedades de configuración, pon Agregar... y añade la propiedad:
Nombre de la propiedad:
hibernate.show_sql
Valor de la propiedad:
true
Dentro de las mismas Propiedades opcionales del modo Diseño de NetBeans IDE, más abajo, en Propiedades varias, Agrega la propiedad:
Nombre de la propiedad:
hibernate.current_session_context_class
Valor de la propiedad:
thread
Después de esto, si ves el archivo hibernate.cfg.xml en el modo Operador XML, quedaría un código más o menos así, pero con otras contraseñas, por supuesto:
Archivos de mapas de Hibernate y POJOs de la base de datos
Crea un Archivo nuevo.... Categoría Hibernar, tipo Archivos de mapas de Hibernate y POJOs de la base de datos.
Debes ponerle nombre a la clase donde estarán los mapas de Hibernate. Para que este tutorial funcione, ponle dvdrental
...y presiona el botón Terminar. Se deberían generar los siguientes archivos:
DVDStore
Source Packages
dvdrental
Actor.hbm.xml
Actor.java
Category.hbm.xml
Category.java
Film.hbm.xml
Film.java
FilmActor.hbm.xml
FilmActor.java
FilmActorId.java
FilmCategory.hbm.xml
FilmCategory.java
FilmCategoryId.java
Language.hbm.xml
Language.java
HibernateUtil.java
Lo último sería crear un Archivo Nuevo..., Categoría Hibernar, Tipo HibernateUtil.java.
Terminar
Empecemos
FilmHelper.java
Crea una nueva Clase Java (Archivo Nuevo..., Categoría Java, tipo Clase Java) y llámale FilmHelper y ponlo dentro del paquete dvdrental.
Terminar
Agrega el siguiente código (en negrita) para crear una sesión de Hibernate.
package dvdrental;
public class FilmHelper {
Session session = null;
public FilmHelper() {
this.session = HibernateUtil.getSessionFactory().getCurrentSession();
}
}
Van a aparecer unos errores que se arreglan usando el menú contextual (botón secundario del ratón) y eligiendo la opción Reparar importaciones.
La opción Reparar importaciones en inglés se llama Fix import.
Si dejamos el valor por defecto, quedaría el código de FilmHelper.java de la siguiente manera:
package dvdrental;
import org.hibernate.Session;
public class FilmHelper {
Session session = null;
public FilmHelper() {
this.session = HibernateUtil.getSessionFactory().getCurrentSession();
}
}
Consultas HQL
Probemos algo. Haz click con el botón secundario sobre el archivo Source Packages/<paquete predeterminado>/hibernate.cfg.xml y selecciona la opción Ejecutar la consulta HQL del menú contextual.
En la barra de herramientas del editor de consultas HQL, selecciona la Sesión: hibernate.cfg y escribe lo siguiente en la primera caja de texto grande:
from Film
Y presiona el botón que está al lado de la lista desplegable que debería estar mostrando hibernate.cfg como opción seleccionada para hacer la consulta y obtener un resultado como el siguiente.
Ahora prueba con una consulta un poquito más complicada:
from Film as film where film.filmId between 100 and 200
Con las consultas probadas, obteniendo los resultados esperados, podemos utilizarlas en la clase helper.
Hibernate
Edita el archivo FilmHelper.java y agrégale el siguiente método (marcado con negrita):
package dvdrental;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
public class FilmHelper {
Session session = null;
public FilmHelper() {
this.session = HibernateUtil.getSessionFactory().getCurrentSession();
}
// Obtiene los films donde el id de film está entre un cierto rango especificado por las variables startID y endID
public List getFilmTitles(int startID, int endID) {
List<Film> filmList = null;
try {
org.hibernate.Transaction tx = session.beginTransaction();
Query q = session.createQuery("from Film as film where film.filmId between '" + startID + "' and '" + endID + "'");
filmList = (List<Film>) q.list();
} catch (Exception e) {
e.printStackTrace();
}
return filmList;
}
}
Repara las importaciones (Fix Import) si es necesario, utilizando las clases de hibernate.
para que quede completamente igual como está en el código de ejemplo anterior.
Vuelve a abrir el Editor de Consultas HQL y haz la siguiente query:
from Actor as actor where actor.actorId in (select filmActor.actor.actorId from FilmActor as filmActor where filmActor.film.filmId='10')
Esta consulta obtiene los actores de la película del filmid = 10.
Ahora súmale el siguiente método (marcado con negrita) al archivo FilmHelper.java:
package dvdrental;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
public class FilmHelper {
Session session = null;
public FilmHelper() {
this.session = HibernateUtil.getSessionFactory().getCurrentSession();
}
// Obtiene los films donde el id de film está entre un cierto rango especificado por las variables startID y endID
public List getFilmTitles(int startID, int endID) {
List<Film> filmList = null;
try {
org.hibernate.Transaction tx = session.beginTransaction();
Query q = session.createQuery("from Film as film where film.filmId between '" + startID + "' and '" + endID + "'");
filmList = (List<Film>) q.list();
} catch (Exception e) {
e.printStackTrace();
}
return filmList;
}
// Obtiene los actores en un film particular
public List getActorsByID(int filmId) {
List<Actor> actorList = null;
try {
org.hibernate.Transaction tx = session.beginTransaction();
Query q = session.createQuery("from Actor as actor where actor.actorId in (select filmActor.actor.actorId from FilmActor as filmActor where filmActor.film.filmId='" + filmId + "')");
actorList = (List<Actor>) q.list();
} catch (Exception e) {
e.printStackTrace();
}
return actorList;
}
}
Repara las importaciones si es necesario. Guarda.
Ahora agrega 3 métodos más y algunos métodos que inventé para hacer uso de beans en el código JSPX.
package dvdrental;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
public class FilmHelper {
Session session = null;
public FilmHelper() {
this.session = HibernateUtil.getSessionFactory().getCurrentSession();
}
// Obtiene los films donde el id de film está entre un cierto rango especificado por las variables startID y endID
public List getFilmTitles(int startID, int endID) {
List<Film> filmList = null;
try {
org.hibernate.Transaction tx = session.beginTransaction();
Query q = session.createQuery("from Film as film where film.filmId between '" + startID + "' and '" + endID + "'");
filmList = (List<Film>) q.list();
} catch (Exception e) {
e.printStackTrace();
}
return filmList;
}
// Obtiene los actores en un film particular
public List getActorsByID(int filmId) {
List<Actor> actorList = null;
try {
org.hibernate.Transaction tx = session.beginTransaction();
Query q = session.createQuery("from Actor as actor where actor.actorId in (select filmActor.actor.actorId from FilmActor as filmActor where filmActor.film.filmId='" + filmId + "')");
actorList = (List<Actor>) q.list();
} catch (Exception e) {
e.printStackTrace();
}
return actorList;
}
// Obtiene una lista de categorías de acuerdo al filmId
public Category getCategoryByID(int filmId) {
List<Category> categoryList = null;
try {
org.hibernate.Transaction tx = session.beginTransaction();
Query q = session.createQuery("from Category as category where category.categoryId in (select filmCat.category.categoryId from FilmCategory as filmCat where filmCat.film.filmId='" + filmId + "')");
categoryList = (List<Category>) q.list();
} catch (Exception e) {
e.printStackTrace();
}
return categoryList.get(0);
}
// Obtiene un solo film de acuerdo al filmId
public Film getFilmByID(int filmId) {
Film film = null;
try {
org.hibernate.Transaction tx = session.beginTransaction();
Query q = session.createQuery("from Film as film where film.filmId=" + filmId);
film = (Film) q.uniqueResult();
} catch (Exception e) {
e.printStackTrace();
}
return film;
}
// Obtiene el idioma del film de acuerdo a un langId
public String getLangByID(int langId) {
Language language = null;
try{
org.hibernate.Transaction tx = session.beginTransaction();
Query q = session.createQuery("from Language as lang where lang.languageId=" + langId);
language = (Language) q.uniqueResult();
} catch (Exception e) {
e.printStackTrace();
}
return language.getName();
}//Métodos para no poner código java en el JSPX
int startId;
int endId;
int filmId;
int langId;
public void setAttributeStartID(int startId){
this.startId = startId;
}
public void setAttributeEndID(int endId){
this.endId = endId;
}
public List getFilmTitlesList(){
return getFilmTitles(startId,endId);
}
public void setAttributeFilmByID(int filmId){
this.filmId = filmId;
}
public List getActorsID() {
return getActorsByID(filmId);
}
public Category getCategoryID() {
return getCategoryByID(filmId);
}
public Film getFilmID() {
return getFilmByID(filmId);
}
public void setAttributeLangByID(int langId){
this.langId = langId;
}
public String getLangID() {
return getLangByID(langId);
}
}
JSP(X)
Hurra. Por fin llegamos a las páginas web. A mi me gusta escribir las JSP con JSTL así que antes de seguir deberás...
Instalar la librería JSTL
Entra a las propiedades del proyecto (botón secundario del ratón sobre DVDStore, opción Propiedades).
En la sección Libraries hay un botón Add Library.... Presiónalo.
Si no te aparece inmediatamente, Importa la librería (o biblioteca también le llaman) JSTL 1.1 o superior.
Selecciona la librería JSTL y añádela al proyecto.
Aceptar
Nuevo archivo JSPX
Primero elimina el archivo index.jsp del proyecto que está en la carpeta DVDStore/Web Pages/ del proyecto.
Ejecuta la aplicación en el servidor, visita la página (en mi máquina se está ejecutando en http://localhost:8084/DVDStore/) y Voilà!. Funciona o te equivocaste.
Conclusiones
A pesar de que este ejemplo sólo hace lecturas a la base de datos, me puedo imaginar que escribir debe ser igual de sencillo ya que autmáticamente se generan la clases bean para insertar valores (set).
Con la persistencia ya no me tengo que preocupar de hacer la conexión y desconexión en cada página, la contraseña está guardada de manera más segura y estandarizada, y el acceso a las tablas se hace sencillo manejándolas como clases (un editor de Java ayuda a completar el código) y no andan las consultas SQL dando vueltas por todo el código, lo que ofrece mayor abstracción entre los distintos niveles del código otorgando flexibilidad a cambios.
Si se me ocurre cambiar la base de datos (agregar una columna, modificar el nombre de una tabla o las relaciones y tipos de datos), habría que regenerar los mapas de Hibernate y volver a compilar todo.
También, si nos fijamos en todo lo que hay que saber sobre programación Java, se hace más difícil la programación, aunque supongo y reconozco que con el tiempo se debe volver más sencillo con la experiencia. Obvio.
Mejoras en el rendimiento... No sé. Supongo que al tener todas las consultas guardadas... Y con Hibernate preocupado de las conexiones y desconexiones... Me imagino que sí hay alguna mejora, lo desconozco.
En el próximo capítulo...
Esto fue cómo leer desde una base de datos con Hibernate, prometo escribir sobre cómo escribir en la base de datos usando Hibernate y hacer este mismo ejemplo usando JPA, que ya lo tengo hecho, pero hay que desarrollarlo y darle formato. Hasta el próximo mes.
Sería bueno explicar qué significa el chistecito del error de capa 8 para los incautos. Lo sospechaba. Aprovecho aquí también de conocer los otros 7 niveles.
El chistecito del error de capa 8 viene del Modelo OSI, que de acuerdo a Wikipedia:
El modelo de referencia de Interconexión de Sistemas Abiertos (OSI, Open System Interconnection) fue el modelo de red descriptivo creado por la Organización Internacional para la Estandarización lanzado en 1984. Es decir, fue un marco de referencia para la definición de arquitecturas de interconexión de sistemas de comunicaciones.
7 capas del modelo OSI
Este modelo presenta 7 capas:
Capa 1: Capa Física
Se refiere al medio físico de transporte como los cables o las ondas, por ejemplo. O también en el proceso de codificar la información para adaptarla al medio físico en que se transportará.
Capa 2: Capa de enlace de datos
La capa de enlace de datos se ocupa del direccionamiento físico, de la topología de la red, del acceso a la red, de la notificación de errores, de la distribución ordenada de tramas y del control del flujo.
Capa 3: Capa de red
Los enrutadores o routers y/o la saturación de la red o paquete.
Capa 4: Capa de transporte
Es cuando se dividen la información en paquetes para ser transportados por la Capa 3, la que se encarga de la comunicación independientemente del medio de transporte de la información.
Capa 5: Capa de sesión
Esta capa es la que se encarga de mantener y controlar el diálogo establecido entre los dos computadores que estan transmitiendo datos de cualquier índole.
Capa 6: Capa de presentación
El objetivo de la capa de presentación es encargarse de la representación de la información, de manera que aunque distintos equipos puedan tener diferentes representaciones internas de caracteres (ASCII, Unicode, EBCDIC), números (little-endian tipo Intel, big-endian tipo Motorola), sonido o imágenes, los datos lleguen de manera reconocible.
Capa 7: Capa de aplicación
Son los protocolos de comunicación como HTTP, SSH, POP, etc. Cabe aclarar que el usuario normalmente no interactúa directamente con el nivel de aplicación. Suele interactuar con programas que a su vez interactúan con el nivel de aplicación pero ocultando la complejidad subyacente.
Error de capa 8 y capa 9
La capa 8 es un chiste usado para referiste a una capa "usuario" o "política" inexistente. Error Capa 8: Error del Usuario. También es conocido como el "error 505" entre el teclado y el asiento. O sea, tú.
Esto es un blog escrito por un tal Oscar desde Iquique (Chile), para compartir y comunicarse con el resto de la comunidad de Internet. También escribo opiniones y HOW-TOs a pedido.