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.