sábado, 15 de diciembre de 2007

Java 1.5/1.6 es Multi-core, ejemplo

Se dijo anteriormente que Java es multi-core, para probar esta aseveración primeramente vamos a definir una tarea que pueda se corrida en paralelo, el siguiente código es simplemente la definición de esta tarea que no tiene nada de extraño en cuanto a la programación multi-thread:

  1  package multicore;
  2  
  3  public class ParallelTask implements Runnable {
  4  
  5     double result = 0 ;
  6  
  7     double taskNo = Math.random()*100 ;
  8  
  9     public void run() {
 10         System.out.println("Starting task No " + Math.floor(taskNo)) ;
 11        
 12         for(int i=0;i<20000000;i++)
 13             result = Math.sin(i) ;
 14         System.out.println("finishing task No " + Math.floor(taskNo)) ;
 15     }
 16  }

Al ser ejecutada la tarea (run) se efectuará la operación trigonométrica seno, unos 20 millones de veces. La idea es esta tarea sea lo suficientemente larga para detectar varias instancias de esta clase ejecutándose en paralelo.

El siguiente código, nuevo para las versiones 1.5 y posteriores, efectivamente crea 6 tareas (pt1, pt2, ...) y las ejecuta en paralelo.

  1  package multicore;
  2 
  3  import java.util.concurrent.Executors ;
  4  import java.util.concurrent.ExecutorService ;
  5 
  6  public class Main {
  7  
  8     public static void main(String[] args) {
  9  
 10         ParallelTask pt1 =new ParallelTask() ;
 11         ParallelTask pt2 =new ParallelTask() ;
 12         ParallelTask pt3 =new ParallelTask() ;
 13         ParallelTask pt4 =new ParallelTask() ;
 14         ParallelTask pt5 =new ParallelTask() ;
 15         ParallelTask pt6 =new ParallelTask() ;
 16 
 17         ExecutorService es =  Executors.newFixedThreadPool(4) ;
 18 
 19         es.execute(pt1) ;
 20         es.execute(pt2) ;
 21         es.execute(pt3) ;
 22         es.execute(pt4) ;
 23         es.execute(pt5) ;
 24         es.execute(pt6) ;
 25 
 26         es.shutdown() ;
 27     } 
 28  
 29  }

Para ejecutar en paralelo primeramente se define el número de cores (newFixedThreadPool(4)), en este caso estamos definiendo 4 cores, si se definen menos cores entonces la aplicación se ejecutará únicamente es ese número de cores quedando los restantes disponibles. Si se definen más de los existentes, algunas tareas simularán multiprocesamiento.

En este caso se han definidos más tareas que cores, en tal caso las tares que “no alcancen en la camioneta” esperarán a que uno de los cores quede dispoble.

La pregunta natural es: ¿como me aseguro que utilizo todos los cores?, yo prefiero utilizar el utilitario “no oficial” de unix/linux “mpstat”. El siguiente es un ejemplo de utilización

$ mpstat -P ALL 10 3

Este comando dará un resumen del desempeño de cada core (CPU), 3 veces cada 10 segundos.

viernes, 7 de diciembre de 2007

Java 1.5/1.6 es Multi-core

SI, definitivamente Java escala con el número de “cores” y lo hace bien. No Había tenido tiempo de revisar, si no hasta ahora, si tenía o no la capacidad de escalar, sabiendo que los procesadores multi-core son populares... o mejor dicho “ya son de todos”.

Cumplo mi tarea en desarrollo de software sobre una estación de trabajo Sun Ultra 40 con SuseLinux 10.3, lo que me permite observar muy detalladamente que ocurre con Java, pues más de un foro señalaba la imposibilidad de escalamiento debido al modelo de memoria de JVM (noticia que parece confirmarse para versiones anteriores a la 1.5).

El problema me resultaba relativamente nuevo. Si bien he hecho programación multi-thread desde hace algunos años, no me había preocupado del tema hasta que debí evaluar el desempeño de un modelo matemático en C/C++/Fortran sobre plataformas Linux/Unix.

Los threads en Java o C# simulan procesamiento en paralelo en un solo procesador. ¿Qué pasa si realmente tenemos más de un CPU en la máquina? ¿dejará de simular y distribuirá el trabajo entre diferentes procesadores? Esto es lo deseable. La primera aplicación que probé hace algunos meses era en C# sobre plataforma Mono.net en que cada thread maneja un sistema independiente de matrices, es decir: no necesito sincronizar datos. Al correr en la estación de trabajo con cuatro cores la plataforma Mono.net reconoció todos y cada unos de ellos y escaló la aplicación en forma prácticamente ideal (insisto: no necesitaba sincronización, caso contrario el escalamiento ya no hubiese resultado tan perfecto), los procesadores eran Opteron dual core de 3 Ghz. Llevé la misma prueba a una máquina con dos cores Xeón de 3 Ghz y el escalamiento fue igual de bueno.

Un artículo de Javalobby, “Java: Multi-core Ready or not?” de Jim Falgout, que plantea que todo está listo en Java para multi-core, me animó a hacer unas cuantas pruebas con Java 1.6 y con Groovy 1.1 RC3 que resultaron exitosas y muy alentadoras. Pude también comprobar que Groovy es el doble de lento que Java y no 30 veces mas como se afirman en algunos foros.

Mas detalles con código en mis próximas entradas de blog.

sábado, 1 de diciembre de 2007

Disculpen mi ignorancia. . .

. . . quisiera hacer las siguientes preguntas:

¿Sabe alguien cuál es la diferencia entre "software gratis" y "software libre"?

¿Y cuál es la relación "software libre" y "código abierto"?

¿Cómo influyen los tipos de licencias en el desarrollo de software?

En algún momento me perdí de los detalles y de pronto todo el mundo comenzó a hablar del asunto, hasta los Presidentes Chávez, Correa y el Evo. (¿Qué tiene que ver el software libre con socialismo?)

Mi preocupación diaria es que el software con el que trabajo se abierto (open source), que por lo general también es gratis (dependiendo de la licencia)...

sábado, 24 de noviembre de 2007

Migrando Stored Procedures a Groovy

Confiezo uno de mis mayores pecados: fui fanático de los Procedimientos Almacenados (stored procedures) en entornos de base de datos, eso fue hasta el año 2001 debido a las críticas no solamente de los autores sino de los gerentes de sistemas con los que trabajaba. De hecho abandonar dicha técnica fue una obligación, ya que un sistema que tube que mantener por 18 meses no los utilizaba (había una prohibición expresa) y tampoco utilizaba vistas !!!.

Había una razón muy importante para ello, el software funcionaba con tres sistemas bases de datos diferentes: Oracle, SqlServer e Interbase según las disponibilidad de cada una de los 43 empresas donde funcionaba el sistema. En tal caso como programador me tocó desempolvar mis conocimientos de estructuras de datos y familiarizarme con UML.

Hoy no voy a detallar los pros y contras de desarrollar con Procedimientos almacenados. Pero si les voy a contar que hace unos meses tube que migrar rápidamente una base de datos a estructuras en memoria construídas con C#. Habían algunos miles de líneas en TransactSql, las que debían ser analizadas cuidadosamente unas veces para mantener la misma lógica y otras para corregirla.

La manera más rápido de hacerlo fue analizar los Cursores, pero no con SQL Server, sino con Groovy.

Los cursores son la razón de ser los lenguajes almacenados.

Miremos la porción de código en TransactSql:

  1  .
  2  .
  3  .
  4  set @anio = 2007
  5  
  6  declare MICURSOR cursor for
  7      select
  8          p.PVEHAMOD as anio_modelo,
  9          c.CVEHCATV as cod_categoria_v,
 10          p.PVEHFECO as fe_co
 11      from
 12          CVEH c,
 13          PVEH p
 14      where
 15              c.CVEHCATV = p.CVEHCATV
 16          and p.PVEHANIO = @anio
 17  
 18  for each row in MICURSOR {
 19      // ...
 20      // manejar cada registro del cursor
 21      // ...
 22  }
 23  .
 24  .
 25  .

Y ahora analicemos el equivalente en Groovy ...

  1  .
  2  .
  3  .
  4  int anio = 2007
  5  
  6  String mainSql = """
  7      select
  8          p.PVEHAMOD as anio_modelo,
  9          c.CVEHCATV as cod_categoria_v,
 10          p.PVEHFECO as fe_co
 11      from
 12          CVEH c,
 13          PVEH p
 14      where
 15           c.CVEHCATV = p.CVEHCATV
 16          and p.PVEHANIO = $anio """
 17  
 18  db.eachRow(mainSql) { rec ->
 19      // ...
 20      // manejar cada registro en la variable rec
 21      // ...
 22  }
 23  .
 24  .
 25  .

Esta estructura simple me permitió extraer la lógica desde unos fuentes en SQL Server para colocarla con mejoras en lenguaje C# y trabajar sobre eficientes tablas texto. Al final de cuentas el problema era matemático y no transaccional sobre base de datos.

sábado, 17 de noviembre de 2007

¿Hacia donde va C#?

Esta pregunta me la hago de vez en cuando mientras trabajo con C#, mientras cumplo mi tarea de programador sobre plataforma Mono en Liux de 64 bits, para ser exacto suseLinux. Es decir las tareas de su creador, Miguel de Icaza, están guiadas por los estándares establecidos por Microsoft.

El resultado es excelente, eso hay que reconocerlo, la misma aplicación la corro en Windows XP, Linux o Solaris... eso es versatilidad

Hace casi tres años que C# incorporó características del emergente lenguaje Ruby, pero la característica inicial y básica cuando apareció a mediados del 2000 era su inconfundible olor a Java y no a C++ como nos quería convencer Bill Gates. En el primer librito de C# que encontré en una librería en Sydney, puse un montón de notas sobre las analogías que encontré con Java, y conste que no encontré todas ya que en ese momento era un programador Delphi/VB6/SQL Server.

Sí, Microsoft y Miguel de Icaza son muy proactivos con su plataformas, pero no las comunidades que tienen volcadas sus espectativas sobre Java y Ruby. Cuando quiero buscar un determinado framework es casi seguro que lo encuentro para Java, y es menos problable que ese mismo framework lo encuentre para .Net.

Hace casi tres años también que el texano Bruce Tate predijo la caída de Java en beneficio de Ruby on Rails. Sun que pareció creerle o en silencio ya lo sabía, reaccionó de inmediato absorviendo de una manera magistral y rápida toda la tecnología Ruby, y sino heche una miradita a Netbeans 6.0.

Entonces si Java fue la inspiración y modelo de C# en el 2000, y para el 2008 Ruby es la inspiración de Java... ¿cual es el camino a seguir de C# este momento en que los conceptos dinámicos (JRuby/Groovy/Ruby, Python/Jython, Rhyno/Javascript) se imponen por su poder y pragmatismo?

miércoles, 7 de noviembre de 2007

Brian Kernighan

Es el coautor de Unix, Lenguaje C y herramientas como AWK, quiero suponer que es el trabajador insignia de Laboratorios Bell. El enlace a su página en dichos laboratorios nos lleva a sus más importantes trabajos:

http://www.cs.bell-labs.com/who/bwk/index.html

Uno de los enlaces nos lleva a un documento muy práctico sobre redes "wireless":

http://www.cs.bell-labs.com/cm/cs/who/bwk/wise/index.html

sábado, 3 de noviembre de 2007

Diseño orientado al almacenamiento de base de datos

¿Qué ocurre cuando se diseña una base de datos para almacenar la estructura de un reporte?. Si los datos sirven únicamente para obtener dicho reporte no hay inconveniente, pero si es la base de la información almacenada de seguro requerirá de transformaciones para adaptar otra funcionalidad, es decir estamos forzados a parchar una estructura que se la requiere más dinámica. La naturaleza de los datos tabulares es vertical, por eso el usuario puede proceder a obtener datos estadísticos, lo que resulta difísil si una tabla estaría dispuesta con ciertas relaciones horizontales. El diseño de base de datos debe seguir las reglas de las "formas normales" siguiendo la naturaleza de las entidades y objetos del mundo real a los que representa, y nada mas. Cuando de reportes se trata hay que saber que se tienen vistas, eso todos lo sabemos, pero el concepto mayor son las herramientas OLAP que efectivamente imponen una transformación de datos para su presentación. Dicha transformación se la hace en otra base de datos que no interfiere en el normal desenvolvimiento de aquella destinada en el almacenamieto excepto para la extracción de datos. Las vistas sobre el almacanamiento pueden resolver un sistema simple de reportes, pero las herramientas OLAP sobre una base de datos transformada con propósitos especiales puede resolver los requerimientos más exóticos de reportes.

sábado, 6 de octubre de 2007

JasperReports/iReports para desarrolladores Java

JasperRepors es una maquinaria interna (engine) para la generacion de reportes en el formato que lo desee, por ejemplo HTML o PDF.

iReports es la herramienta gráfica para el diseño de reportes que después serán ejecutados y producidos por una aplicación Java mediante APIs de JasperReports.

Venía trabajando con el par JasperReports/iReports desde inicios del 2006, la primera versión con la que había trabajado fue 1.2.6. La experiencia fue un poco difícil ya que las guías de usuarios eran dos o tres "tutoriales" sin una descripción de los APIs, y lo peor de todo los forums eran muy limitados.

La nueva versión 2.0.1 llego acompañada de dos libros que son distribuidos por Apress (también están disponibles como e-books por US$45), que nos da información casi completa no solamente de los APIs si no de todo lo que una herramienta de reportes debe hacer:

  • Reportes simples
  • Subreportes
  • Integración con Java
  • Gráficos estadísticos
  • Tablas cruzadas
  • Integración con fuentes OLAP
  • ...etc

Siga los enlaces, en estos días son mi herramienta favorita

http://jasperforge.org/sf/projects/ireport

http://www.apress.com/book/view/1590599276

http://www.apress.com/book/view/1590599284

Uno de los puntos débiles en la información es que no explica el proceso de distribución e implantación (“desployment” en inglés), donde la generación de reportes ya no debe depender de la herramienta gráfica iReports.

miércoles, 5 de septiembre de 2007

linux: mount + sshfs

Recientemente evalué unos equipos en forma remota desde mi estacion de trabajo con Suse Linux 10.2. Si bien tenía servicio ftp habilitado para transferir archivos entre las dos máquinas, era más cómodo si "montaba" el sistema de archivos remoto.

Para lograrlo debí instalar el utilitario sshfs, luego cree el directorio local /media/temp

# mkdir /media/temp

luego ejecuté el siguiente comando

# modprobe fuse

y finalmente realizo el montaje

# sshfs root@nnn.nnn.nnn.nnn:/home /media/temp

al buen entendedor pocas palabras: me conecto como usuario "root" de la máquina remota con dirección IP nnn.nnn.nnn.nnn. Además quiero que el directorio remoto "/home" esté montado sobre el directorio local "/media/temp". luego de ejecutado este comando me pide el password del usuario root remoto.

Para desmontar:

# umount /media/temp

viernes, 3 de agosto de 2007

El código bonito ...

El "código bonito" es uno de los tantos -y útiles- portales de O´Reilly, en este caso está relacionado al libro "homónimo"

http://beautifulcode.oreillynet.com/

por ejemplo, si con frecuencia debe crear "singletons", ud. debe saber que son muy útiles, pero de allí a que realmente sean "bonitos" al menos merecemos una explicación, y aqui va una... "Singletons are beautiful because they rely on an interesting interplay between access protection and static scope" es decir "los singletons son bonitos porque dependen en un interesante juego entre acceso protegido y un entorno estático"...

ooops... quedemos que los singleton son prácticos y concretos, especialmente si se los debe explotar cuando se requiere estructuras únicas en un ambiente de producción al que se ha sometido un software específico, por ejemplo todo lo relacionado a configuración.

sábado, 14 de julio de 2007

Sobre los retos de la persistencia

El "ceviche mixto" lo había planteado como una mezcla del lenguajes utilizados por el programador, por ejemplo código Java o C# salpicado de sentencias SQL que se llaman mediante el mecanismo de conectividad como JDBC o ADO.NET.

Grafiquemos el problema en un sistema de punto de ventas, una factura está compuesta por un encabezado donde se colocan los detalles del negocio, del cliente y del número de factura, luego viene una lista de los productos y por último el pié de página donde se observa el sub-total, los descuentos, el impuesto y el gran total.

Tradicionalmente un módulo de ventas, es un conjunto de sentencias SQL que son llamadas desde, digamos, una aplicación hecha en Delphi. Si la sentencia SQL es para generar un nuevo número de factura, se la ejecuta y se coloca el resultado en algún control visual (una caja de texto o etiqueta). El asunto suena muy trivial, la consulta es sobre una tabla de facturas de donde toma la última venta en base a la cual calcula el nuevo número. Pero que tal si el dueño del negocio le quiere un cambio y expresa “quiero que en el número de factura conste la fecha y un número secuencial, por ejemplo 20070708-001, 20070708-002, 20070708-003, etc.”. El esquema empieza a desmoronarse, porque además la estructura de facturación del sistema también puede cuestionarse.

La adaptación del sistema de punto de ventas (POS) en el mejor de los casos comienza a ser re-elaborado.

El software monolítico no tiene cura, cae por su propio peso, pero un software bien diseñado separa la lógica de negocios de la mecánica de persistencia. Un objeto es el que vive los algoritmos almacenados en sus métodos, pero otro es el que toma su estado y los persiste (almacena en una base de datos, en un archivo, etc.) o recupera el estado persistido para generar un nuevo objeto de negocios. Ambas clases son cambiables, si el entorno de un sistema implica persistencia sobre Oracle, en otro puede ser MySql, eso tiene incidencia en la mecánica de persistencia no en la lógica de negocios, es decir que otro criterio clave será todo aquello relacionado con el polimorfismo e Interfaces.

viernes, 22 de junio de 2007

persistencia: la clase persistible

...quiere decir una clase cuyos instancias (objetos) puedan salvar su estado en un medio persistente (como un archivo o base de datos) para un momento posterior se pueda recrear ese objeto.

Me propongo en esta ocasión guardar el estado de los objetos en alguna base de datos (los ejemplos aquí expuestos están en MySql).

Miremos primeramente el lado de la base de datos (el modelo relacional). Me enseñaron en la Poli lo importante que es el álgebra relacional y la normalización, que se resume en la definición de las tablas, una clave primaria, una o más clave alternas y externas así como cuidar de la integridad relacional entre tablas. Todo esto mas un buen criterio en modelamiento es la base de conocimiento de todo niño informático que dice de sí mismo ser un “hacker”.

Hace años, en algún momento aprendí que era mejor no perder tiempo en la búsqueda una clave primaria, y dejar que cada tabla tenga una columna con valores enteros únicos a la que se la llama “Id” y que sea éste campo la clave primaria. Se incrementa en 1 cada vez que se agrega un nuevo registro. Esto no impide que invierta mi tiempo en buscar índices en base a claves únicas que son la base de la eficiencia.

Por ejemplo: en una tabla con la información de licencias de manejo al menos tendrá dos claves: Id, que es la clave primaria, y el número de licencia que es una clave alterna única. Crear un nuevo registro impone un nuevo valor Id sobre una base secuencial (máximo Id + 1). Actualizar un registro significa buscarlo en base al campo Id para actualizarlo. Cargar un registro implica aplicar un criterio de búsqueda, encontrarlo y recordar su Id con el propósito de actualizarlo o borrarlo. A esto voy a agregar otra columna de control,”version”, de tal manera que cada que ejecute una sentencia sql “update…” este campo se incrementa en la unidad.

     create table licencia (
         Id int,
         version int,
         numero varchar(16),
         -- otros campos
   primary key(Id));
     )

 (ingresando datos tendríamos...)

  +------+---------+------------+- - -
  | Id   | version | numero     | ....
  +------+---------+------------+- - -
  |    1 |       1 | 2190033333 |
  |    2 |       1 | 9012227615 |
  |    3 |       1 | 7810289387 |
  +------+---------+------------+- - -

Miremos ahora la definición de la clase (para variar... en groovy). Si quiero que un objeto sea persistible, entonces no se me ocurre otra cosa que replicar los campos “Id” y “version” del modelo relacional a la definición de clase:

  1  class licencia {
  2      int Id
  3      int versión
  4      String numero
  5      // ... otros campos
  6      // ... otros métodos
  7  }

Esta clase no debería contener sentencias SQL relacionadas con persistencia para mantenerla como elemento del dominio de la aplicación, que absorba todo la complejidad de la lógica de negocios sin mezclar código de persistencia.

Entonces... ¿dónde está el código que carga (load), guarda (save) o elimina (delete) un objeto?, en otra clase llamada licencia_ORM que contiene todos esos métodos, y que debería manejarse a nivel de comandos:

  1  licencia_ORM orm = new licencia_ORM()
  2  licencia lic = new Licencia()
  3 
  4  lic = orm.load('Id = 1')
  5  //
  6  // operaciones del dominio de negocios sobre el objeto 'lic'
  7  //
  8  orm.save lic

para los iniciados

Por algunas razones prefiero este esquema de persistencia a algunos frameworks:

  • Menos dependencias
  • La clase licencia_ORM es generable y es el punto claro donde defino las sentencias SQL de persistencia.
  • Excepto por los campos “Id” y “version” la clase licencia en el dominio de negocios está alejada de anotaciones que afectan la visibilidad del código.
  • Se me ocurre que aspectos de la clase licencia_ORM pueden ser controlados mediante "metaclase" en groovy.
  • Este mecanismo lo utilicé inicialmente en Delphi y créanme ¡ jamás me ha fallado !

miércoles, 20 de junio de 2007

persistencia

Persistencia es el "arte" de almacenar y/o recuperar el estado de un objeto, y el estado de un objeto es el valor de los campos de una objeto (mil disculpas si algún purista se siente incómono con estas definiciones).

No todo campo de un objeto requiere persistir, puede ser calculado, en tal caso a ese campo se lo llama trasciente. Por ejemplo si tengo una clase que representa un triángulo rectángulo entonces el estado a almacenarse corresponderá a los dimensión de los lados, mientras que la hipotenusa es un campo trasciente ya que resulta de aplicar la eterna ecuación pitagórica.

¿Pero cómo se almacena un objeto?, la respuesta puede ser tan sencilla como por ejemplo implementar un método "save()" en una clase de tal manera que abra un archivo, escriba allí el valor de cada campo, y luego cierre el archivo. Luego si requiero recuperar el estado de un objeto, leo desde el archivo el estado de los campos previamente almacenados. Pero es sabido que si se pretende almacenar la información en una base de datos, por las facilidades de indexación los métodos "load()" y "save()" no van a ser muy tribiales.

Una serie de metodologías y patrones se aplican bajo la denominación "Mapeo Objeto-Relacional" (en Inglés Object Relational Mapping - ORM). Esta denominación tiene algunas implicaciones la principal es aquella que nos dice que el modelo de negocios es un modelo de objetos (orientado a objetos) mientras que el almacenamiento es un modelo relacional. Cuando un objeto persiste movemos el estado de un modelo a otro.

A pesar de la eficiencia alcanzado por los RDBMS como MySql, DB2, Oracle, Sql Server, etc. El modelo relacional parece algo superable a largo plazo, es decir que pasará algún tiempo hasta que tengamos un producto serio y seguro para almacenar (persistir) objetos sin recurrir al modelo relacional. Por el momento ORM significa bastante. Un programador con mediana experiencia puede sospechar y deducir mentalmente alguna metodología para almacenar objetos, pero el tema se puede tornar tan escabroso y complejo que hoy por hoy tenemos algunos frameworks, como Hibernate e iBatis. Si se ha dado una vuelta por la librerías habrá notado el grosor de libros de Hibernate, pero si quiere saber que tan debatido y profundo es el tema le recomiendo este interasantísimo artículo.

martes, 19 de junio de 2007

¿.Net o Java?

...quiere decir una clase cuyos instancias (objetos) puedan salvar su estado en un medio persistente (como un archivo o base de datos) para un momento posterior se pueda recrear ese objeto.

Me propongo en esta ocasión guardar el estado de los objetos en alguna base de datos (los ejemplos aquí expuestos están en MySql).

Miremos primeramente el lado de la base de datos (el modelo relacional). Me enseñaron en la Poli lo importante que es el álgebra relacional y la normalización, que se resume en la definición de las tablas, una clave primaria, una o más clave alternas y externas así como cuidar de la integridad relacional entre tablas. Todo esto mas un buen criterio en modelamiento es la base de conocimiento de todo niño informático que dice de sí mismo ser un “hacker”.

Hace años, en algún momento aprendí que era mejor no perder tiempo en la búsqueda una clave primaria, y dejar que cada tabla tenga una columna con valores enteros únicos a la que se la llama “Id” y que sea éste campo la clave primaria. Se incrementa en 1 cada vez que se agrega un nuevo registro. Esto no impide que invierta mi tiempo en buscar índices en base a claves únicas que son la base de la eficiencia.

Por ejemplo: en una tabla con la información de licencias de manejo al menos tendrá dos claves: Id, que es la clave primaria, y el número de licencia que es una clave alterna única. Crear un nuevo registro impone un nuevo valor Id sobre una base secuencial (máximo Id + 1). Actualizar un registro significa buscarlo en base al campo Id para actualizarlo. Cargar un registro implica aplicar un criterio de búsqueda, encontrarlo y recordar su Id con el propósito de actualizarlo o borrarlo. A esto voy a agregar otra columna de control,”version”, de tal manera que cada que ejecute una sentencia sql “update…” este campo se incrementa en la unidad.

     create table licencia (
         id int,
         version int,
         numero varchar(16)
         -- otros campos
     )

 (ingresando datos tendríamos...)

 +------+---------+------------+--- - - -
 | Id   | version | numero     | ....
 +------+---------+------------+--- - - -
 |    1 |       1 | 2190033333 |
 |    2 |       1 | 9012227615 |
 |    3 |       1 | 7810289387 |
 +------+---------+------------+--- - - -

Miremos ahora la definición de la clase (para variar... en groovy). Si quiero que un objeto sea persistible, entonces no se me ocurre otra cosa que replicar los campos “Id” y “version” del modelo relacional a la definición de clase:

class licencia {
    int Id
    int versión
    String numero
    // ... otros campos
    // ... otros métodos
}

Esta clase no debería contener sentencias SQL relacionadas con persistencia para mantenerla como elemento del dominio de la aplicación, que absorba todo la complejidad de la lógica de negocios sin mezclar código de persistencia.

Entonces... ¿dónde está el código que carga (load), guarda (save) o elimina (delete) un objeto?, en otra clase llamada licencia_ORM que contiene todos esos métodos, y que debería manejarse a nivel de comandos:

licencia_ORM orm = new licencia_ORM()
licencia lic = new Licencia()

lic = orm.load('Id = 1')
//
// operaciones del dominio de negocios sobre el objeto 'lic'
//
orm.save lic

para los iniciados

Por algunas razones prefiero este esquema de persistencia a algunos frameworks:

martes, 12 de junio de 2007

Persistencia en Groovy...

En las últimas 2 semanas he estado trabajando sobre una mini clase de persistencia que la tengo casi lista, es una versión groovy de un esquema que lo he utilizado con éxito en Delphi, Java y C#... en los próximos días lo expondré. Saludos JP

domingo, 3 de junio de 2007

¡ rápido con groovy ! operaciones básicas con base de datos.

Ahora que hemos creado una clase re-utilizable para controlar la conectividad de la base de datos, podemos plantearnos un conjunto de operaciones básicas utilizando SQL, por ejemplo voy a utilizar la conectividad MySql para crear una base de datos, los pasos son (1). lograr la conectividad, (2) crear la base de datos, (3) Activar la base de datos creada, (4) crear una tabla dentro de dicha base de datos, (5) ingresar unos datos a la tabla creada, (6) desplegar los datos, (7) alterar un registro específico en la base de datos, (8) desplegar otra vez los datos, (9) Eliminar un registro, (10) desplegar otra vez los datos y (11) desconectarse de la base de datos.

Miremos primeramente como serían estas operaciones sin el soporte de groovy, utilizando únicamente sentencias SQL:

 -- (2) creación de la base de datos, de nombre  DBGROOVY
 create database DBGROOVY ;

 -- (3) Activar la base de datos creada,
 use DBGROOVY ;

 -- (4) crear una tabla dentro de dicha base de datos, la tabla Libro
 create table Libro (
     Id int,
     version int,
     titulo varchar(30),
     Autor varchar(30)
 ) ;

 -- (5) ingresar unos datos a la tabla creada,
 insert into Libro values (1,1,'Nuestro Hombre en la Habana','Greene') ;
 insert into Libro values (2,1,'La cándida Eréndira...','García Marquez') ;
 insert into Libro values (3,1,'Amares','Galeano') ;

 -- (6) desplegar los datos de la tabla Libro
 select * from Libro ;

 -- (7) alterar un registro específico en la base de datos,
 update Libro set  Autor = 'Graham Green' where Id = 1 ;
 update Libro set  Autor = 'Gabriel García Marquez' where Id = 2;
 update Libro set  Autor = 'Eduardo Galeano' where Id = 3 ;

 -- (8) desplegar otra vez los datos
 select * from Libro ;

 -- (9) Eliminar un registro,
 delete from Libro where Id = 2 ;

 -- (10) desplegar otra vez los datos
 select * from Libro ;

 -- Elimino la base de datos DBGROOVY
 drop database DBGROOVY ;

Al correr este código desde la herramienta "MySql Command Line Client" obtengo el siguiente resultado

 Enter password: *****
 Welcome to the MySQL monitor.  Commands end with ; or \g.
 Your MySQL connection id is 1 to server version: 5.0.18-nt


 mysql> -- (2) creación de la base de datos, de nombre  DBGROOVY
 mysql> create database DBGROOVY ;
 Query OK, 1 row affected (0.20 sec)

 mysql>
 mysql> -- (3) Activar la base de datos creada,
 mysql> use DBGROOVY ;
 Database changed
 mysql>
 mysql> -- (4) crear una tabla dentro de dicha base de datos, la tabla Libro
 mysql> create table Libro (
     ->     Id int,
     ->     version int,
     ->     titulo varchar(30),
     ->     Autor varchar(30)
     -> ) ;
 Query OK, 0 rows affected (0.40 sec)
 
 mysql>
 mysql> -- (5) ingresar unos datos a la tabla creada,
 mysql> insert into Libro values (1,1,"Nuestro Hombre en la Habana","Greene") ;
 Query OK, 1 row affected (0.06 sec)
 
 mysql> insert into Libro values (2,1,"La cándida Eréndira...","García Marquez");
 Query OK, 1 row affected (0.05 sec)
 
 mysql> insert into Libro values (3,1,"Nuestro Hombre en la Habana","Galeano") ;
 Query OK, 1 row affected (0.05 sec)
 
 mysql>
 mysql> -- (6) desplegar los datos de la tabla Libro
 mysql> select * from Libro ;
 +------+---------+-----------------------------+----------------+
 | Id   | version | titulo                      | Autor          |
 +------+---------+-----------------------------+----------------+
 |    1 |       1 | Nuestro Hombre en la Habana | Greene         |
 |    2 |       1 | La cándida Eréndira...      | García Marquez |
 |    3 |       1 | Amares                      | Galeano        |
 +------+---------+-----------------------------+----------------+
 3 rows in set (0.00 sec)
 
 mysql>
 mysql> -- (7) alterar un registro específico en la base de datos,
 mysql> update Libro set  Autor = "Graham Green" where Id = 1 ;
 Query OK, 1 row affected (0.07 sec)
 Rows matched: 1  Changed: 1  Warnings: 0
 
 mysql> update Libro set  Autor = "Gabriel García Marquez" where Id = 2;
 Query OK, 1 row affected (0.04 sec)
 Rows matched: 1  Changed: 1  Warnings: 0
 
 mysql> update Libro set  Autor = "Eduardo Galeano" where Id = 3 ;
 Query OK, 1 row affected (0.06 sec)
 Rows matched: 1  Changed: 1  Warnings: 0
 
 mysql>
 mysql> -- (8) desplegar otra vez los datos
 mysql> select * from Libro ;
 +------+---------+-----------------------------+------------------------+
 | Id   | version | titulo                      | Autor                  |
 +------+---------+-----------------------------+------------------------+
 |    1 |       1 | Nuestro Hombre en la Habana | Graham Green           |
 |    2 |       1 | La cándida Eréndira...      | Gabriel García Marquez |
 |    3 |       1 | Amares                      | Eduardo Galeano        |
 +------+---------+-----------------------------+------------------------+
 

 mysql>
 mysql> -- (9) Eliminar un registro,
 mysql> delete from Libro where Id = 2 ;
 Query OK, 1 row affected (0.04 sec)

 mysql>
 mysql> -- (10) desplegar otra vez los datos
 mysql> select * from Libro ;
 +------+---------+-----------------------------+-----------------+
 | Id   | version | titulo                      | Autor           |
 +------+---------+-----------------------------+-----------------+
 |    1 |       1 | Nuestro Hombre en la Habana | Graham Green    |
 |    3 |       1 | Amares                      | Eduardo Galeano |
 +------+---------+-----------------------------+-----------------+
 2 rows in set (0.00 sec)
 
 mysql>

Ahora nos planteamos las mismas sentencias SQL ejecutadas desde Groovy

  1    import DBConnection.*
  2    
  3    // (1) lograr la conectividad a MySql
  4    Connection MySqlCN = ConnectionFactory.getConnection('MySQL')
  5    MySqlCN.connect('localhost','MySql','root','admin')
  6    
  7    // (2) creación de la base de datos, de nombre DBGROOVY
  8    MySqlCN.db.execute 'create database DBGROOVY'
  9    
 10    // (3) Activar la base de datos creada
 11    MySqlCN.db.execute 'use DBGROOVY'
 12    
 13    // (4) crear una tabla dentro de dicha base de datos, la tabla Libro'
 14    MySqlCN.db.execute '''
 15        create table Libro (
 16             Id int,
 17             version int,
 18             titulo varchar(30),
 19             Autor varchar(30)
 20         ) '''
 21    
 22    // (5) ingresar unos datos a la tabla creada
 23    MySqlCN.db.execute "insert into Libro values (1,1,'Nuestro Hombre en la Habana','Greene')"
 24    MySqlCN.db.execute "insert into Libro values (2,1,'La cándida Eréndira...','García Marquez')"
 25    MySqlCN.db.execute "insert into Libro values (3,1,'Amares','Galeano')"
 26    
 27    // (6) desplegar los datos de la tabla Libro
 28    MySqlCN.db.eachRow('select * from Libro') { rs ->
 29        println "$rs.Id.- $rs.version, $rs.titulo, $rs.Autor"
 30    }
 31    
 32    // (7) alterar un registro específico en la base de datos
 33    MySqlCN.db.execute "update Libro set  Autor = 'Graham Green' where Id = 1"
 34    MySqlCN.db.execute "update Libro set  Autor = 'Gabriel García Marquez' where Id = 2"
 35    MySqlCN.db.execute "update Libro set  Autor = 'Eduardo Galeano' where Id = 3"
 36    
 37    // (8) desplegar otra vez los datos
 38    MySqlCN.db.eachRow('select * from Libro') { rs ->
 39        println "$rs.Id.- $rs.version, $rs.titulo, $rs.Autor"
 40    }
 41    
 42    // (9) Eliminar un registro
 43    MySqlCN.db.execute "delete from Libro where Id = 2"
 44    
 45    // (10) desplegar otra vez los datos
 46    MySqlCN.db.eachRow('select * from Libro') { rs ->
 47        println "$rs.Id.- $rs.version, $rs.titulo, $rs.Autor"
 48    }
 49    
 50    // borra la base de datos DBGROOVY
 51    MySqlCN.db.execute 'drop database DBGROOVY'
 52    
 53    db.disconnect()
 54    
 55    return true

Algunas cosas que explicar:

La primera: estamos utilizando la librería de conectividad en DBConnection.groovy

Segunda: uno de los Aureliano Buendía, el que huye de su natal Macondo en "100 años de soledad", llega a los brazos y se enamora de la cándida Eréndira, quien ha sido prostituida por su desalmada Abuela quie a su vez sobrevive a un atentado planeado por los tórtolos! ... "hierva mala nunca muere".

Terecera: sobre la ejecución de sentencias SQL, si éstas no retornan ningún registro o valor escalar entonces se utiliza el método ".execute(...)", si en cambio se ejecuta una consulta que retorna uno o más registros, o un elemento escalar, se utiliza el método ".eachRow(...)". Pasemos a un corto análisis del siguiente código:

 27    // (6) desplegar los datos de la tabla Libro
 28    MySqlCN.db.eachRow('select * from Libro') { rs ->
 29        println "$rs.Id.- $rs.version, $rs.titulo, $rs.Autor"
 30    }
  • El método .eachRow(...) toma como argumento una consulta en SQL.
  • Cada registro es retornado y almacenado en una estructura, que en nuestro ejemplo es "rs".
  • Dinámicamente esta estructura captura y genera los campos equivalentes a la tabla consultada en base de datos, esa es la razón por la cual se observan los elementos "rs.Id", "rs.version", "rs.titulo" y "rs.Autor"
  • La estructura "rs" no es nada parecido a un Registro Activo en el sentido del lenguaje Ruby

eachRow(), por devolver un solo registro a la vez, resulta óptimo a la hora de recuperar y procesar grandes cantidades de datos. Pero si requiere recuperar un conjunto de datos y almacenarlos en una lista existen otras opciones que serán exploradas en el próximo blog. Hece una semanas requerí extrare 57 millones 600 mil datos de una tabla en una base de datos para transformarlo en un archivo CSV, eachRow() trabajó de manera muy óptima en cuanto a velocidad de procesamiento y bajo consumo de memoria.

domingo, 27 de mayo de 2007

¡ rápido con groovy ! Un esquema de conectividad JDBC completo

En el blog anterior propuse encapsular la conectividad, primero para esconder algún nivel de complejidad, y segundo con el propósito de construir una estructura reutilizable para más de un sistema de base de datos

La estructura propuesta (gráfico) tiene que ver con una clase abstracta que contine la lógica pre-elaborada y normalizada para lograr un punto de comunicación con cualquier sistemas de base de datos RDBMS que "hable" JDBC. El usuario de esta clase utilizará el método connect(...) para logar una conexión activa, internamente se llama al método setServer(..) a ser definido en una subclase concreta orientada a un RDBMS específico, en nuestro caso las clases concretas son SQLSERVERConnection y MySqlConnection, para SqlServer y MySql respectivamente.

  1  ...
  2  // Conectividad abstracta
  3  abstract class Connection {
  4 
  5    abstract void setServer(server,DB)
  6 
  7    boolean connect(erver,DB,user,password) {
  8      ...
  9      setServer(server,DB)  
 10      ...
 11    }
 12    ...
 13  }
 14 
 15 
 16  // Conectividad MS-SqlServer
 17  class SQLSERVERConnection extends Connection {
 18 
 19    void setServer(server,DB) {
 20      ...
 21    }
 22 
 23  }
 24 
 25 
 26  // Conectividad MySQL
 27  class MySqlConnection extends Connection {
 28 
 29    void setServer(server,DB) {
 30      ...
 31    }
 32 
 33  }

Como se vió en el blog anterior las subclases concretas se limitan a definir los campos driver y url de acuerdo a cada RDBMS. Si desea conectarse a Oracle, primero deberá encontrar e instalar las librerías de conectividad JDBC, agregar a CLASSPATH la localización de los archivos JAR y luego crear la clase concreta que la podriamos llamar OracleConnection.

Para lograr un buen nivel de adaptabilidad, una "factoría" parece lo más indicado. Esta no es otra cosa que una clase con un método estático, en este caso getConnection(...), que retorna una instancia de una conexión específica.

  1  class ConnectionFactory {
  2 
  3    static Connection getConnection(dbType) {
  4      if(dbType == 'MySQL')
  5          return new MySqlConnection()
  6      else
  7      if(dbType == 'MS-SqlServer')
  8          return new SQLSERVERConnection()
  9      else
 10          return null ;
 11    }
 12 
 13  }

Honestamente ésta es una factoría bastante simple. La idea es que en base al argumento el método retorne una instancia de conexión específica

  1  // recupera una conexión para MySql
  2  Connection cn = ConnectionFactory.getConnection('MySql')
  3 
  4  // recupera una conexión para SqlServer
  5  Connection cn = ConnectionFactory.getConnection('MS-SqlServer')

A continuación se presenta el código completo, al final hay un código de prueba, que deberá ser comentado o eliminado luego de las respectivas pruebas. Este código lo he guardado en un archivo de nombre DBConection.groovy.

  1  import groovy.sql.Sql                                    
  2                                                           
  3                 
  4  // == Abstract connection ==        
  5                 
  6  abstract class Connection {         
  7                 
  8  def db              
  9                 
 10  def driver             
 11                 
 12  def url              
 13                 
 14  abstract void setServer(strServer,strDB)     
 15                 
 16  boolean connected = false         
 17                 
 18  boolean connect(strServer,strDB,strUser,strPassword) {  
 19    try {              
 20      if(connected)           
 21        disconnect()           
 22      setServer(strServer,strDB)        
 23      db = Sql.newInstance(url,strUser,strPassword,driver) 
 24      connected = true          
 25                 
 26    } catch(Exception ex) {         
 27      db = null            
 28      connected = false          
 29    }               
 30                 
 31      return connected          
 32    }               
 33                 
 34                 
 35    void disconnect() {          
 36      db.close()            
 37      db = null            
 38    }               
 39                 
 40  }               
 41                 
 42                 
 43  // == MS-SqlServer connection ==       
 44                 
 45  class SQLSERVERConnection extends Connection {    
 46                 
 47    void setServer(strServer,strDB) {                          
 48      driver = 'com.microsoft.jdbc.sqlserver.SQLServerDriver'                 
 49      url = "jdbc:microsoft:sqlserver://$strServer:1433;databaseName=$strDB"  
 40    }                                  
 51                 
 52  }               
 53                 
 54                 
 55  // == MySQL connection ==         
 56                 
 57  class MySqlConnection extends Connection {     
 58                 
 59    void setServer(strServer,strDB) {       
 60      driver = 'com.mysql.jdbc.Driver'      
 71      url = "jdbc:mysql://$strServer:3306/$strDB"    
 72    }               
 73                 
 74  }               
 75                 
 76                 
 77  // === Factory Class ===         
 78                 
 79  class ConnectionFactory {         
 80                 
 81    static Connection getConnection(dbType) {     
 82      if(dbType == 'MySQL')         
 83        return new MySqlConnection()       
 84      else             
 85      if(dbType == 'MS-SqlServer')       
 86        return new SQLSERVERConnection()      
 87      else             
 88        return null ;           
 89    }               
 90                 
 91  }               
 92                 
 93                 
 94                 
 95  // - código de prueba : inicio
 96 
 97  def myServer = 'localhost'
 98  def myDBType = 'MySQL'
 99  def myUser = 'root'
100  def myPassword = 'admin'
101 
102  Connection myConection = ConnectionFactory.getConnection(myDBType)  
103 
104  if(myConection.connect(myServer,myDBType,myUser,myPassword)) {
105    println '¡ Bacán ! me conecté a $myDBType en $myServer !!!'
106    myConection.disconnect()
107  }
108  else
109    println 'uff... no pude Conectarme a $myDBType en $myServer '
110 
111  return true // - código de prueba : fin

Para compilar estas clases almacenadas en el archivo "DBConection.groovy", procedo desde la línea de comandos:

   groobyc DBConection.groovy

que genera un archivo *.class por cada clase

Para "los iniciados"...

...estoy totalmente de acuerdo que:

  • es posible evitar la herencia si leemos parámetros de un archivo de configuración
  • es necesario declarar una interface con el contrato de implementación
  • es posible y necesario parametrizar los puertos IP
  • si se soporta en Hibernate, IBatis, etc. puede que Ud. esté haciendo las cosas de otra manera

miércoles, 23 de mayo de 2007

! rápido con groovy ! más sobre conectividad

Encapsular la conectividad

En el primer intento por encapsular mi único esfuerzo por parametrizar es el usuario y el password de acceso a la base de datos. La conexión sigue destinada a un servidor y a una base de datos determinados en la variable url. Además la base de datos debe ser MySql. El método connect será el encargado de iniciar la conexión.

  1   import groovy.sql.Sql
  2 
  3 
  4   class Connection {
  5 
  6     def db
  7 
  8     def driver = 'com.mysql.jdbc.Driver'
  9 
 10     def url = 'jdbc:mysql://localhost:3306/mySql'
 11 
 12     def connected = false
 13 
 14     boolean connect(user,password) {
 15       try {
 16         if(connected) disconnect();
 17         db = Sql.newInstance(url,user,password,driver)
 18         connected = true
 19 
 20       } catch(Exception ex) {
 21         db = null
 22         connected = false
 23       }
 24 
 25       return connected
 26     }
 27 
 28 
 29     void disconnect() {
 30       db.close()
 31     }
 32   }
 33 
 34 
 35   // -- código de prueba
 36   Connection myConection = new Connection()
 37 
 38   if(myConection.connect('root','admin')) {
 39     println 'Conectado a MySql'
 40     myConection.disconnect()
 41   }

El tipo de base de datos: MySql, Oracle, DB2, SqlServer, etc. al cual se destina la conectividad está definido en las variables driver y url. Se me ocurre que debe haber un método, al que llamaré setServer(...) para definir dichos parámetros. Además si dejo que dicho método sea abstracto (por lo que la clase debe necesariamente ser abstracta), entonces podré crear subclases, conexiones concretas, orientadas a un determinados tipo de base de datos. La conexión base abstracta es la siguiente...

  1   import groovy.sql.Sql
  2 
  3 
  4   abstract class Connection {
  5 
  6     def db
  7 
  8     def driver
  9 
 10     def url
 11 
 12     abstract void setServer(strServer,strDB)
 13 
 14     boolean connected = false
 15 
 16     boolean connect(Server,DB,User,Password) {
 17       try {
 18         if(connected)
 29           disconnect()
 20         setServer(Server,DB)
 21         db = Sql.newInstance(url, User, Password, driver)
 22         connected = true
 23 
 24       } catch(Exception ex) {
 25         db = null
 26         connected = false
 27       }
 28 
 39       return connected
 30     }
 31 
 32 
 33     void disconnect() {
 34       db.close()
 35       db = null
 36     }
 37 
 38   }

A continuación una subclase concreta para conectarnos a MySql

  1   class MySqlConnection extends Connection {
  2 
  3     void setServer(Server,DB) {
  4       driver = 'com.mysql.jdbc.Driver'
  5       url = "jdbc:mysql://$Server:3306/$DB"
  6     }
  7 
  8   }

A continuación una subclase concreta para conectarnos a MS SqlServer

  1   class SQLSERVERConnection extends Connection {
  2 
  3     void setServer(strServer,strDB) {
  4       driver = 'com.microsoft.jdbc.sqlserver.SQLServerDriver'
  5       url = "jdbc:microsoft:sqlserver://$strServer:1433; databaseName=$strDB"
  6     }
  7 
  8   }

Para conectarse a SqlServer es prerequisito que las localización de librerías msbase.jar, msutil.jar, mssqlserver.jar consten en la variable de sistema CLASSPATH. Esta librerías son parte de la instalación JDBC SqlServer descargable del portal de Microsoft.

lunes, 21 de mayo de 2007

¡ rápido con groovy ! Conéctese a la base de datos

el prerequisito...

Primero debe tener instaladas las librerías de conectividad JDBC para el sistema de base de datos remoto o local que va a utilizar. Para el caso de MySql, he instalado la librería correspondiente en el directorio:

C:\JAVALIBS\mysql-connector

el archivo "jar" que nos interesa es

mysql-connector-java-3.1.7-bin.jar

la localización de esta librería debe estar especificada en la variable de entorno CLASSPATH que sirve para proveer la lista de librerías Java disponibles en su entorno separadas por punto y comas. Asegúrese que se encuentre el archivo en mención, si no entonces colóquelo al final de la lista

... ; ... ; ... ; C:\JAVALIBS\mysql-connector\mysql-connector-java-3.1.7-bin.jar

está listo para conectarse a MySql desde groovy !

El siguiente código es suficiente para lograr la conexión a la base de datos

Conexión rápida a MySql

  1   import groovy.sql.Sql
  2 
  3   def url = 'jdbc:mysql://localhost:3306/mySql'
  4   def user = 'root'
  5   def password = 'admin'
  6   def driver = 'com.mysql.jdbc.Driver'
  7   def db = Sql.newInstance(url,user,password,driver)
  • url especifica el protocolo JDBC, la base de datos mysql, el servidor localhost, el puerto IP de comunicaciones 3306} y la base de datos mySql
  • User es el usuario definido para lase de datos
  • password es la clave de ingreso autorizada para el servidor y la base de datos especificada en el URL.
  • El driver hace referencia a la librería utilizada

La variable db sostendrá la conexión luego de ejecutarse el método Sql.newInstance. Con esta conexión se podrían ejecutar sentencias DDL como crear una base de datos, cambiar la base de datos activa, crear una tabla, etc. mediante el método execute. Con el método eachRow se pueden hacer consultas SQL así como otras operaciones.

  1 
  2   // crea una base de datos
  3   db.execute "create database myDB"
  4 
  5   // cambia de base de datos activa
  6   db.execute "use myDB"
  7 
  8   // realiza una consulta SQL
  9   db.eachRow("SELECT * FROM ....") { }
 10 
 11   // cierra la conexión
 12   db.close()

El problema es que el código de conectividad expuesto no está parametrizado y no es reutilizable, por lo tanto necesitamos... "encapsular" la conectividad

MIL DISCULPAS, el blog original estubo tan laaaaargo que debí cortarlo.

miércoles, 16 de mayo de 2007

¡ rápido con groovy ! GroovyBeans para programadores Java y C#

representando una clase libro

Así es como se ve una clase en Groovy:

  1    class Libro {
  2 
  3      String titulo
  4 
  5      String getTitulo() {
  6          return titulo
  7      }
  8 
  9      void setTitulo(String tit) {
 10          titulo = tit
 11      }
 12    }

se parece mucho a Java y a C#, excepto que:

  • no necesito de puntos y comas para separar sentencias
  • el campo titulo es preconfigurado como privado como todo campo
  • los métodos getTitulo() y setTitulo(...) son preconfigurados como públicos al igual que todo método

La clase aquí formada es la encapsulación de un solo campo, titulo, por lo tanto los métodos get... y set... son sus accesores, que además están en un formato muy común.

Para evitarle esta molestia de estar programado get... y set... la mayoría de los IDE´s (como los míos: NetBeans para Java y SharpDevelop para C#) están en capacidad de generarlos automáticamente.

  1    class Libro {                    
  2                                     
  3        String titulo                
  4                
  5        String getTitulo() {         
  6            return titulo            
  7        }                            
  8                                     
  9        void setTitulo(String tit) { 
 10            titulo = tit             
 11        }                            
 12    }                                

Groovy le va a evitar la molestia a Ud. y a su IDE favorito, las declaraciones muy obvias get... y set... pasan a ser implícitas.

  1    class Libro {                    
  2                                     
  3        String titulo                
  4                
  5    }                                

Si no se convence todavía, por favor corra el programa:

  1    class Libro {                    
  2        String titulo                
  3    }                                
  4                                     
  5    miLibrito = new Libro()          
  6    miLibrito.setTitulo('Amares')    
  7    println miLibrito.getTitulo()    

Y si lo que extraña es la notación de propiedad de C# y VB.NET, simplemente reemplace la llamada a los métodos set... y get... con la notación "bean":

  1    class Libro {                    
  2        String titulo                
  3    }                                
  4                                     
  5    miLibrito = new Libro()          
  6    miLibrito.titulo = 'Amares'      
  7    println miLibrito.titulo         

En tal caso recuerde que no está accesando directamente al campo, sino los métodos implícitos get... y set... con notación "Bean".

Es posible redefinir el comportamiento predeterminado e implícito de los accesores si lo que se quiere es un método más especializado:

  1    class Libro {                    
  2                                     
  3        String titulo                
  4                                     
  5        void setTitulo(String tit) { 
  6            titulo = tit.toUpperCase() 
  7        }                            
  8    }                                
  9                                     
 10    miLibrito = new Libro()          
 11    miLibrito.titulo = 'Amares'      
 12    println miLibrito.titulo         

En este ejemplo se redefine la función set... con el propósito de transformar a mayúsculas el valor del campo. No se requiere redefinir el método get. Trate de hacer lo mismo en Java o C# y compare el número de líneas y el nivel de complejidad del código. En Groovy se programa menos líneas de código que en esos lenguajes pero debe ser conciente de las declaraciones implícitas y dinámicas.

domingo, 13 de mayo de 2007

Mi selección de lenguajes dinámicos...

JavaScript y Groovy... el primero como una herramienta esencial de los navegadores y el segundo como un Java muy evolucionado. Me voy a referir a Groovy. Fue por Octubre del 2005 cuando me convencí del valor de una herramienta llamada Ruby on Rails, o simplemente Rails, para elaboración de aplicaciones web, sobre la base de lenguaje Ruby. Ruby es a Rails lo que C# es a ASP.NET o lo que Java es a JSP. Aunque no hay que perder de vista que Rails es una importante innovación tecnológica. Los autores comenzaron a hablar de Rails, el mismo portal de Sun hablaba de una “sana envidia”. Claro está que para saber Rails primero hay que saber Ruby, así que a comprar librito y ponerse a leer, soy programador y no puedo dejarme vencer por los sorpresivos advenimientos en tecnología. Ruby es un lenguaje tan viejo o nuevo como Java, es muy dinámico y bien evolucionado, algunos autores lo señalan como el más avanzado de las plataformas de desarrollo de software. La versión 2.0 de C# ya incluyó algunas de sus características pero no Java, básicamente por los debates a veces fundamentalistas en su comunidad de desarrolladores y autores. Fue revisando algunas referencia relacionadas con Ruby que llegué a Groovy, y de inmediato me resultó más innovador, Groovy es un Ruby innovado que corre sobre tecnología Java, además no necesitaba votar mis conocimientos a la basura para aprender algo nuevo. Las innovaciones en tecnología de desarrollo de software son el resultado de procesos evolutivos. En Java, la ventaja aprovechable es una plataforma flexible basada en su máquina virtual JVM, Java Virtual Machine, algo parecido se puede decir de .Net . La compilación del código genera otro código (bytecode) que es ejecutado por la máquina virtual. ¿Qué tal si el código que compila no es Java sino un lenguaje más dinámico y menos estricto?. La idea no es nueva, en 1998 leí de un lenguaje llamado NetFlex de IBM, que no era otra cosa que su lenguaje Rexx sobre JVM. La lista de tales lenguajes sigue creciendo, y básicamente es la reproducción de lenguajes dinámicos con todo su potencia sobre JVM: Rhino, Jyton, Groovy, etc, etc. Groovy ha estado en desarrollo por cerca de 4 años, y la versión 1.0 nos llegó solamente en enero del 2007, pan caliente, pero la adopción corporativa estuvo en marcha desde el 2004 como lo dejan ver los portales de Sun e IBM. La estructura y la idea central de Ruby conforman el corazón de Groovy mas un importante valor agregado en creatividad. Groovy convive con Java y todos sus entornos como Spring y Hibernate, etc y herramientas como JasperReports, JUnit y Ant. Dicha convivencia debería ser la razón para una adopción más rápida en las aplicaciones corporativas. Personalmente lo utilizo para generar y transformas rápidamente la información de la base de datos, me es útil a la hora de manejar HTML y XML con un enfoque más pragmático casi siempre utilizando un mecanismo que es justamente lo que necesitaba: “Builder”. Aunque todas las opciones y capacidades de groovy son muy interesantes, los “Builders” son particularmente útiles por permitir representar estructuras jerárquicas con código apropiado y evidente. También me a causado grata impresión la capacidad de integración con aplicaciones externas. Grails es a Groovy lo que Rails es a Ruby. Es un buen momento para atrapar conocimiento sobre Grails que está en pleno desarrollo. El concepto principal es “programación por convención”, en la que se le pide hacer las cosas de una determinada manera para generar el código necesario: “dígame QUE es lo que quiere que yo se COMO lo hago”. ¿Algún Ruby para .Net? Las plataformas .Net como Microsoft o Mono, posiblemente son más abundantes en lenguajes pero menos en entornos. Hoy en día puede que sea Boo el equivalente de Ruby, o debe haber algo que se llame NRuby, honestamente no tengo planeado evaluar todos y cada uno de los lenguajes dinámicos sobre plataformas Java y .Net. De todas maneras Microsoft y la comunidad alrededor de Mono.Net reconocen muy bien los momentos de innovación tecnológica y no deberían estar lejos de lograr su Ruby con valor agregado y capacidad integradora. ¿Está Java en proceso de extinción? Definitivamente NO, pero el lenguaje está definitivamente separado de la máquina virtual (físicamente siempre lo estuvo), eso está claro para todos. Las plataformas de desarrollo NetBeans y Eclipse son mas que entornos de desarrollo (IDE) el espacio donde se integran tecnologías relacionadas a Java y nunca pudieron estar mejores gracias a los esfuerzos comunitarios y corporativos. Yo estoy esperando que NetBeans de soporte a Groovy, el problema mayor es justamente la ausencia de un entorno apropiado de programación. Para Eclipse este problema puede haber sido resuelto las últimas semanas. Cada nueva versión de Java tiene una cantidad enorme de adiciones que refuerzan la plataforma y dan al usuario ventajas cada vez mayores.

jueves, 10 de mayo de 2007

Horroroso SPANGLISH

en inglés se dice "by default" en spanglish se dice "por default" en español "por defecto" aunque la traducción debería ser "de fábrica" considero errónea la traducción "por defecto" ... mientras que "de fábrica" puede resultar confusa sobre todo a la hora de tratar con patrones. ¿no sería mejor recurriir al latín para bucar una traducción elegante de la frase en Inglés "by default"? ¿alguna recomendación?

sábado, 5 de mayo de 2007

Ojo a los Lenguajes Dinámicos

Las características dinámicas de los lenguajes de programación no son nuevas, y las experiencias al respecto se han acumulando en los últimos años. ¿Qué hace que un lenguaje de programación sea dinámico?.

Desde mi experiencia cotidiana en la elaboración de software, lo primero que asumo con un lenguaje de programación orientado a objeto y dinámico es que todo lo obvio sea implícito, y lo implícito es un comportamiento claro y evidente en la ejecución de la aplicación sin la necesidad de haber escrito código al respecto. Sin embargo si necesito alterar ese comportamiento “por default”, estaré en plena capacidad de redefinir tal comportamiento sobre la base y estructura impuesta por el entorno de programación.

El conjunto de características dinámicas es un importante valor agregado a un lenguaje orientado a objeto.

Quienes diseñan lo dinámico analizan y atrapan los mecanismos cognocitivos de nosotros los programadores, un factor común de ese análisis (¿o psicoanálisis?) da como resultado un conjunto de elementos que pueden ser dinamicos. Por ejemplo, si millones de programadores están dedicados a elaborar aplicaciones con soporte de base de datos, entonces la lógica de los movimientos transaccionales debería ser la parte implícita. En forma concreta el lenguaje proveerá un Registro Activo dinámicamente generado y que representará un registro específico de una tabla en la base de datos. La ventaja de un registro activo, es que esconderá el proceso de recuperar, actualizar y guardar información de la base de datos en comandos como load, update, delete y save, sin que el programador tenga que incrustar sentencias con el popular SQL (el ceviche mixto Java/SQL, C#/SQL o VB/SQL expresado como una exagerada mezcla de sentencias SQL en el código del programa, es un plato pecaminoso y marca con la sentencia de muerte a muchos sistemas de software, y por ser ceviche es el plato favorito de los programadores locales y consta en el menú de carreras universitarias rápidas).

La primera ventaja para el programador es menos líneas de código, pero el reto es conocer más conceptos, las ventajas dinámicas están asociadas íntimamente con las características orientadas a objeto, en definitiva un perfil profesional alto del programador. Para la economía de la organización todo esto supone pagar menos por mantenimiento de software, mejor adaptabilidad y mayor tiempo de vida útil de sus aplicaciones de software.

En esta ocasión he evitado una definición formal para Lenguaje Dinámico, la wikipedia acusa tales definiciones de ambiguas pero nos da un interesante listado de características que se merecen un rápido análisis histórico.

Desde mi perspectiva de programador en plataformas Java y .Net tengo mi propia selección de lenguajes dinámicos, ¿cuales y porqué? lo expondré en los próximos días.

miércoles, 2 de mayo de 2007

Elcomercio.com le da la bienvenida a sus usuarios a este espacio de opinión.