Mostrando entradas con la etiqueta Java. Mostrar todas las entradas
Mostrando entradas con la etiqueta Java. Mostrar todas las entradas

viernes, 1 de febrero de 2008

equals y hashCode en Hibernate

La clase java.lang.Object, de la cual heredan todas las demás clases, define dos métodos muy importantes:

public boolean equals(Object obj)
public int hashCode()

El API de Java describe un conjunto de reglas que deben cumplir ambos métodos en caso de sobreescribirlos, la idea del post no es repetir esos fundamentos teóricos, sino plantear una receta muy sencilla para implementarlos.

Como norma siempre los sobrescribo en todas las entidades usando como clave de comparación una combinación de atributos con significado de negocio (business key) que me asegure la unicidad del objeto.
Si usamos la funcionalidad de id autogenerado (generator class="native"), debemos descartar éste atributo porque es asignado luego de que el objeto es salvado, hasta ese momento el id es null.

Para implementar los métodos me baso en la librería Commons Lang de Apache, es muy probable que ya tengan el jar en el classpath si usan otros frameworks open source, en caso contrario es solo bajarlo del sitio web.

Vamos al grano, por ejemplo si tenemos una entidad Pais (id, codigoISO, nombre), el atributo candidato que nos asegura la unicidad es el código ISO, por lo tanto la implementación sería:


public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(this.codigoISO)
.toHashCode();
}

Instanciamos el HashCodeBuilder con dos números primos diferentes y luego hacemos append de los atributos que definen la business key, en éste caso solamente el codigoISO.

public boolean equals(Object object) {
Pais otroPais = (Pais)object;
return new EqualsBuilder()
.append(this.codigoISO, otroPais.getCodigoISO)
.isEquals();
}

Instanciamos el EqualsBuilder y luego hacemos append de la comparación de los atributos que definen la business key, en éste caso solamente el codigoISO.

Para no tener comportamientos inesperados en la aplicación, es recomendable siempre sobreescribir los métodos equals y hashCode en todas las entidades persistentes, usando ésta librería nos aseguramos que lo hacemos de manera consistente.

Nota: En los ejemplos falta contemplar la posibilidad de NullPointerException o ClassCastException, eso lo dejo como tarea ;-)

Hasta el próximo post!!

viernes, 11 de enero de 2008

Identificación de entregables en Java

Es importante que los aplicativos en producción desplieguen en algún lugar visible su número de versión, de ésta forma cuando los usuarios reportan incidentes, podemos solicitar que incluyan el "Program-Version" para intentar reproducir el problema en nuestro ambiente de desarrollo.

Por ejemplo, en las aplicaciones web desarrolladas en Java siempre trato de incluir en el war (el componente que se instala en el servidor), un timestamp que sirva como clave de identificación de la versión y a la vez permita saber en que fecha fue generado.
Ésto lo resuelvo en la tarea ANT que empaqueta el war:


<tstamp><format property="hoy" pattern="yyyyMMddHHmmss" locale="en"/></tstamp>
<target name="make.war" depends="build">
<jar destfile="aplicacion.war" >
<manifest>
<attribute name="Manifest-Version" value="1.0"/>
<attribute name="Program-Version" value="${hoy}"/>
</manifest>
<fileset dir="${webapp.dir}"/>
</jar>
</target>

Como resultado, el war queda con un archivo llamado MANIFEST.MF con un formato clave=valor (Program-Version=20071218112500, si lo generé el 18 de diciembre de 2007 a las 11:25 de la mañana) dentro del directorio META-INF. Luego podré leerlo y mantenerlo en memoria desde un servlet, por ejemplo:

URL manifestURL = this.getServletContext().getResource("/META-INF/MANIFEST.MF");
Manifest mf = new Manifest(manifestURL.openStream());
Attributes attr = mf.getMainAttributes();
String programVersion = attr.getValue("Program-Version");
this.getServletContext().setAttribute("programVersion", programVersion);

y desde una página JSP podré desplegarlo simplemente usando un tag JSTL, por ejemplo:

<c:out value='${programVersion}'/>

Espero que les sea de utilidad, hasta el próximo post!

miércoles, 26 de diciembre de 2007

Criteria Query en Hibernate

Uno de los puntos débiles que encontré en el nuevo estándar JPA (Java Persistence API de Sun), es que no provee una funcionalidad análoga al Criteria Query de Hibernate.

El Criteria Query nos permite definir consultas con un estilo orientado a objetos, muy distinto al clásico SQL o HQL. La primera impresión del API me resultó un poco extraña, difícil de leer. Por ejemplo, el siguiente query con Criteria:


List personas = sess.createCriteria(Persona.class)
.add(Restrictions.like("nombre", "Fede%"))
.add(Restrictions.isNull("edad"))
.addOrder(Order.asc("nombre"))
.list();
sería el equivalente a éste otro con HQL:

String query = "from Persona where nombre like 'Fede%' and edad is not null order by nombre asc";
List personas = sess.createQuery(query).list();

Sin dudas el query HQL es mas fácil de leer a simple vista, la versión orientada a objetos requiere un análisis mas detallado.

Pero por otro lado, que sucedería si la consulta tuviera que armarse dinámicamente?
Por ejemplo, si el usuario selecciona las opciones por las cuales desea realizar la búsqueda, entonces el "where" del query debemos establecerlo en tiempo de ejecución. En éste caso es dónde personalmente le encuentro una gran ventaja al Criteria porque podemos hacer cosas como:

Criteria criteria = sess.createCriteria(Persona.class);
if (nombre != null) {
criteria.add(Restrictions.eq("nombre", nombre));
}
if (edad != null) {
criteria.add(Restrictions.eq("edad", edad));
}
if (pais != null) {
criteria.add(Restrictions.eq("pais", pais));
}
List personas = criteria.list();

El código anterior define una consulta que toma como entidad base a "Persona" y luego dependiendo de si hay un valor asignado para nombre, edad y país agrega las restricciones al Criteria.

Para resolverlo en HQL deberíamos armar el query concatenando strings, que no está mal, pero puede inducir a errores en la definición, me parece que en estos casos es mas natural usar el estilo orientado a objetos del Criteria.

Además permite desarrollar un esquema de consultas genérico, donde el usuario del sistema "arma" su propio query sin mayor esfuerzo por parte del desarrollador, en próximos posts publicaré ejemplos.

miércoles, 12 de diciembre de 2007

Manejo de Fechas en Java

Éste es un tema que puede parecer básico, pero usualmente veo ejemplos en la web que utilizan estrategias totalmente "retorcidas" para formatear fechas o hacer operaciones con las mismas.

Desde hace un tiempo uso un framework que facilita el manejo de fechas en Java y se puede integrar muy bien con Hibernate. Le podemos decir adiós a las "transformaciones" entre java.util.Date y java.sql.Date, también al manejo del GregorianCalendar.

El nombre del framework puede resultar un poco extraño en español, pero les aseguro que no es una broma, se llama Joda Time.
Con solo agregar el jar en el classpath tenemos acceso a una infinidad de opciones para trabajar con fechas, por ejemplo:

  • Sumar/restar días, meses o años a una fecha
  • Obtener la diferencia en días entre dos fechas
  • Saber el día de la semana de una fecha (lunes, martes, miércoles...)
Siempre trato de no usar el API directamente, por lo general tengo dos clases que las llamo:
  • DateOnly (soporta manejo de fecha)
  • DateTime (soporta manejo de fecha-hora)
las mismas delegan las invocaciones a una instancia particular del framework, así evito estar acoplado al producto.

Para integrar el manejo de fechas con Hibernate, implementé dos UserType, éstas clases son las que tienen la lógica de mapear los atributos DateOnly de las entidades con sus correspondientes columnas en la base de datos.

Mas adelante voy a tratar de subir algún ejemplo de código para bajar un poco mas en detalle.
Les dejo el link de la documentación oficial.

Hasta el próximo post!!

Model Driven Architecture en Java

Simplificando su definición, la metodología MDA (Model Driven Architecture, especificada por el OMG) propone definir la funcionalidad de las aplicaciones de forma abstracta, independientemente de tecnologías, por ejemplo con UML; y luego dada la especificación de alto nivel, se podría "traducir" la misma a una implementación concreta en Java o .NET mediante una herramienta automatizada.

Hay varias herramientas que siguen el paradigma MDA, la mayoría muy verdes en mi opinión, aunque si leemos las presentaciones prometen el paraíso, los proveedores le "ponen color" a sus productos, como decimos en Uruguay.

Por ejemplo, el año pasado evalué AndroMDA, es una herramienta que permite tomar un modelo UML y transformarlo en componentes deployables para plataformas como J2EE y .NET. También incluye un kit para desarrollar generadores de código o personalizar los existentes.

En un momento estuve cerca de empezar a usar AndroMDA en mi trabajo, pero no me pareció conveniente porque ya teníamos un esquema de desarrollo definido y la personalización de los templates no era sencilla, en ese momento no había documentación suficiente, como pasa en muchos de los proyectos open source.
Por lo tanto me basé en varios artículos sobre code generation para desarrollar algo in-house.

Si analizamos los cronogramas de los proyectos, veremos que dedicamos un tiempo considerable en implementar ABMs (Alta, Baja, Modificación), cuando por lo general es una funcionalidad accesoria. Lo realmente importante son los casos de uso que involucran procesos de negocio, por ejemplo registrar una transacción bancaria.
Basándome en la premisa anterior, lo natural era comenzar a estudiar la automatización del desarrollo de los mantenimientos.

Lo primero fue llegar a un ABM estándar (como el "Work With" de Genexus), por ejemplo con: búsqueda, ingreso, detalle, modificación y eliminación de usuarios; siempre tratando de estandarizar los nombres de los componentes (páginas JSP, clases Java, etc).

El siguiente paso fue identificar los lugares que hacían referencia a la entidad en juego, para así poder definir un template y usar expresiones en esos lugares.

Luego del ejercicio obtuvimos los siguientes componentes de software:

  1. Usuario.java: Clase del dominio que define los atributos del usuario, declara los mapeos de Hibernate mediante XDoclet.
  2. ABMUsuarioAction.java: Action de Struts con todas las operaciones.
  3. UsuarioForm.java: ActionForm de Struts con los mismos atributos que la clase de dominio.
  4. usuarioForm.jsp: Página de ingreso, detalle y modificación.
  5. usuarioList.jsp: Página con filtro de búsqueda y grilla de resultado con acciones.
  6. UsuarioServices.java: Interface que define los servicios de la entidad.
  7. UsuarioServicesImpl.java: Clase que implementa la interface de servicios.
  8. UsuarioDAO.java: Clase que implementa las operaciones sobre la base de datos.
Como primera aproximación, podríamos crear los archivos del 2 al 8 a partir de la clase de dominio usando introspection para descubrir los atributos de la entidad, por supuesto si usamos el estándar JavaBean para nombrar getter y setters.

Como template engine elegí Velocity, me pareció muy sencillo usar, tiene un lenguaje llamado VTL que incluye estructuras de control, operadores y expresiones.
Por ejemplo, para generar la interface de servicio, definimos el siguiente template (resumido):

public interface ${upperEntityName}Services {
static final ${upperEntityName}Services INSTANCE = new ${upperEntityName}ServicesImpl();

${upperEntityName} buscarByID(Long id);
void eliminar(Long id);
void ingresar(${upperEntityName} ${lowerEntityName});
void modificar(${upperEntityName} ${lowerEntityName});
List buscarTodos();
}

Lo que está dentro de ${} son expresiones, Velocity maneja un contexto en el que podemos insertar objetos con un nombre asociado, en el ejemplo sería upperEntityName y lowerEntityName.

Para invocar al generador usé ANT, la tarea solicita al usuario el nombre de la clase de dominio con su paquete incluido y a partir de la misma usa los templates de Velocity para crear todos los archivos en los directorios correspondientes.

Hemos llegado a automatizar un 95% del desarrollo de los ABMs, siempre tratando de que el generador deduzca la mayor cantidad de cosas posibles a partir del modelo de dominio. Por ejemplo si la entidad define un atributo de tipo Date, es casi seguro que en la JSP de ingreso necesitaremos una caja de texto con un calendario javascript a la derecha y una validación por fecha.

El proceso de desarrollo "copy, paste & replace" se puede optimizar y si invertimos esfuerzo en automatizarlo, no hay dudas que se gana mucho en productividad.

Hasta el próximo post!!

viernes, 7 de diciembre de 2007

Scheduler en Java

Algunas aplicaciones necesitan que ciertos procesos se disparen automáticamente, sin intervención del usuario, en éstos casos precisamos una herramienta que nos permita planificar su ejecución.

En Java hay varios frameworks que funcionan como "Scheduler" o "Planificador de Tareas", personalmente he usado en varios proyectos uno que se llama Quartz, y con muy buenos resultados. Es sencillo de configurar y no depende del ambiente en donde corre el aplicativo, funciona en todos los application servers, al menos en Tomcat y en JBoss no he tenido inconvenientes.

Los pasos básicos para configurar Quartz son:

1. Bajar el framework desde la web del proveedor e incluir las librerías requeridas en el aplicativo
2. Crear una clase que implemente la interface Job y en el método execute escribir la lógica que necesitamos ejecutar periódicamente
3. Instanciar un SchedulerFactory (se debería hacer cuando el aplicativo esta iniciándose) y registrar el job anterior. Por ejemplo si en una aplicación web tenemos un listener o un servlet que se ejecuta por única vez podemos usar el siguiente código:

StdSchedulerFactory factory = new StdSchedulerFactory();
Scheduler scheduler = factory.getScheduler();
scheduler.start();
JobDetail jobDetail = new JobDetail("exampleJob", Scheduler.DEFAULT_GROUP, ExampleSchedulerJob.class);
int hour = 17;
int minute = 50;
Trigger trigger = TriggerUtils.makeDailyTrigger(hour, minute);
trigger.setName("exampleTrigger");
scheduler.scheduleJob(jobDetail, trigger);
this.getServletContext().setAttribute("QUARTZ_SCHEDULER_KEY", scheduler);


En éste caso registramos la clase ExampleSchedulerJob (es la que tiene la lógica del proceso, implementa la interface Job) para que se ejecute todos los días a las 17:50 (makeDailyTrigger).
Además la clase TriggerUtils brinda varias opciones para configurar tareas semanales, mensuales, etc.

Para implementar casos mas avanzados les recomiendo la documentación de Quartz, es muy completa.

Hasta el próximo post!

lunes, 3 de diciembre de 2007

Guerra de plataformas

Es algo que se da inevitablemente, las discusiones sobre cual es la "mejor plataforma de desarrollo" se repiten constantemente, por lo general no conducen a nada porque prevalecen los dogmatismos y las personas tendemos a cerrarnos en nuestra posición y a no escuchar a los demás, bueno .... en realidad lo mismo pasa cuando hablamos de fútbol o política.

Actualmente considero que hay tres opciones consolidadas para desarrollar software empresarial a medida: Java, .NET o herramientas tipo CASE (por ejemplo Genexus), cada una con sus ventajas y desventajas.
Personalmente conozco mucho mas el mundo Java porque trabajo en él desde hace varios años, pero también he desarrollado en C# y soy analista Genexus, en cuanto a cual es mejor no tengo una respuesta definitiva, depende mucho del contexto del proyecto y de los recursos materiales/humanos que disponemos.

Lo que si es claro es que se han ejecutados proyectos exitosos con todas las tecnologías, en todo caso los fracasos se dan generalmente a causa de un relevamiento de requerimientos pobre o por una mala conformación del equipo de trabajo, no porque el proyecto se desarrolló en .NET en lugar de Java o viceversa.

Como dice el técnico de fútbol argentino Bambino Veira: "Se ha salido campeón con todos los sistemas tácticos, lo importante es tener buenos jugadores, que estén motivados y convencidos de lograr el objetivo", yo aplicaría esta frase también en nuestro ámbito.

Hasta el próximo post!

miércoles, 28 de noviembre de 2007

Carrera de Ratas en el Desarrollo de Software

Hace unos meses leí parte de un libro llamado “Rich Dad, Poor Dad” escrito por Robert Kiyosaki, en el que entre otras cosas habla sobre el círculo vicioso en el que entramos las personas por querer ganar mas dinero con el objetivo de mejorar el nivel de vida, pero a su vez para mantenerlo debemos trabajar cada vez mas y de ésta forma perdemos el foco de lo realmente importante.

Pero... ¿que tiene que ver ésto con la informática? ... bueno, en éste caso encuentro que lo que Kiyosaki llama carrera de ratas también se aplica al área del desarrollo de software, que es a lo que me dedico. No se si ustedes compartirán, pero siento que muchas veces nos distraemos en estar "agiornados" con las últimas tecnologías en lugar de concentrarnos en lo que realmente importa: resolver las necesidades de los clientes cumpliendo plazos de entrega y presupuesto con un producto de calidad. En la empresa donde trabajo tengo la libertad de incorporar nuevas herramientas, trato de estar atento a las tendencias del mercado pero solamente las incorporo si realmente agregan valor, me refiero a que el fundamento no se limite a "estar a la moda".

Por ejemplo, desde hace varios años desarrollo aplicaciones web basadas en Struts e Hibernate, utilizando una metodología basada en MDA (Model Driven Architecture), en posts futuros entraré en detalles. El hecho es que éste entorno lo tengo automatizado y hemos logrado una productividad alta, entonces me cuestiono si necesito migrar a JSF (Java Server Faces) o a otros productos novedosos como GWT (Google Web Toolkit). Tomando en cuenta las necesidades de los clientes, hasta ahora no hay razones que justifiquen el cambio, si bien se pueden encontrar críticas sobre Struts y comparaciones con otros frameworks MVC en las que puede salir perdiendo, personalmente convivo con sus defectos (que no son tantos) y aplico workarounds casi transparentes que no requieren demasiado esfuerzo. Otra cosa sería si comenzara a desarrollar desde cero, sin un know how acumulado, en este caso seguramente lo evaluaría desde otra perspectiva.

Por otro lado con Hibernate no tengo dudas, es por lejos el mejor framework open source que conozco, para quienes continúan usando JDBC como estrategia de persistencia les recomiendo que evalúen el cambio, pero tengan en cuenta que lleva tiempo dominarlo, no es cuestión de "tirarse a la piscina" en un proyecto complejo con recursos acotados.

Para terminar este post y como conclusión, creo que si bien no hay que estar al margen de las novedades, como profesionales debemos cada vez mas evaluar el costo/beneficio en su incorporación y evitar entrar en la "carrera de ratas" sin tener claro hacia donde nos lleva, simplemente por estar en la "cresta de la ola" o enriquecer nuestro CV.

domingo, 25 de noviembre de 2007

Primer Post

En éste blog intentaré compartir mi experiencia en el mundo de la Informática y además escribiré sobre otros temas de interés general. Soy uruguayo y me desempeño en el área de las Tecnologías de la Información, especialmente bajo la plataforma Java.