martes, 12 de junio de 2007
Persistencia en Groovy...
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.