Groovy i HSQLDB

Groovy aporta classes per al treball amb bases de dades que permeten un increment de la productivitat en el desenvolupament de codi d’aplicacions que en facin us.

Dit d’una altre forma: Groovy permet simplificar el codi de les aplicacions de bases de dades.  Res millor que un exemple.

En l’exercici que es proposa faré us de la base de dades HSQLDB, que és el motor de base de dades que es fa servir, per exemple,  a les suites ofimàtiques OpenOffice.org i LibreOffice.

Per a poder utilitzar HSQLDB des de Groovy cal afegir el hsqldb.jar a les llibreries que es carreguen a l’inici. En el cas que m’ocupa, un Groovy sobre Linux Ubuntu, cal revisar el fitxer $GROOVY_HOME/conf/groovy-starter.conf en el que es pot trobar el següent:

    # load user specific libraries
    load !{user.home}/.groovy/lib/*.jar

Tinc l’HSQLDB instal·lat a una carpeta apart. Per tant, per evitar duplicitats, en comptes de posar l’hsqldb.jar a $HOME/.groovy/lib, el que faig és posar en aquesta carpeta un enllaç simbòlic al jar que hi ha a la carpeta on està desplegat el motor de la base de dades.

A continuació, creo una base de dades del tipus fitxer i standalone. Cal dir que HSQLDB pot funcionar en “mode servidor” o  bé en “mode standalone”. A més, també pot funcionar o bé mantenint les dades en un fitxer, o bé “mantenint” les dades en memòria (evidentment, només es “mantenen” mentre el procés és viu).

Per a crear la taula es pot fer servir, per exemple, l’OpenOffice.org/LibreOffice Base. En aquest cas caldria afegir hsqldb.jar al classpath de l’OOo/LO (eines – opcions – java)

i camí a les classes:

i, a continuació, crearia una base de dades del tipus Jdbc.

En el meu cas, la base de dades la fico a Documentes/databases/hsqldb/prova.db, per tant la URL JDBC que he de fer servir és:


jdbc:hsqldb:/home/albert/Documents/databases/hsqldb/prova.db

i el driver:

org.hsqldb.jdbc.JDBCDriver

(canviar aquesta imatge)

Això em permetrà fer servir l’assistent de l’OOo/LO per a definir la taula i els tipus de dades. Creo una taula diccionari amb tres columnes: id (enter), nom (varchar de 100) i valor (varchar de 100).

I  un cop creada la taula, la informo amb algunes dades de prova:

Probablement la classe més útil de Groovy per al treball amb bases de dades sigui groovy.sql.Sql.  Aquesta classe és una mena de navalla suïssa que proporciona una varietat de mètodes per a fer gairebé totes les tasques comuns amb taules.

La pàgina de la documentació de groovy.sql.Sql ens explica les possibilitats d’aquesta navalla suïssa.

Per a utilitzar-la  cal importar-la

import groovy.sql.Sql

A continuació puc configurar la connexió a la base de dades que he creat fa un moment:

def ds = [url:’jdbc:hsqldb:/home/albert/Documents/databases/hsqldb/prova.db’, 
          user:’sa’, 
          password:”, 
          driver:’org.hsqldb.jdbc.JDBCDriver’]
def sql = Sql.newInstance(ds.url, ds.user, ds.password, ds.driver)

Consulto la taula que he creat amb el OOo/LO amb el potent mètode eachRow, adonem-nos com la fila en curs és accessible amb ‘it’:

sql.eachRow(“select * from public.\”diccionari\” where \”id\” >= 3″) {
    println “id = ${it.id}; nom = ${it.nom}; valor = ${it.valor}”
}

i el resultat és

albert@athena:~/Workspace/wk-groovy/prova-jdbc$ groovy provabd.groovy 
id = 3; nom = nom3; valor = valor3
id = 4; nom = nom4; valor = valor3
id = 5; nom = nom5; valor = valor5

Creo una nova taula i la informo amb dades. Cal tenir en compte algunes particularitats de l’SQL de  l’HSQLDB: els noms de les taules han d’anar precedides de l’esquema; els noms de taules, columnes i  d’altres objectes van entre cometes dobles; els literals van entre comentes simples.

sql.execute ”’
create table public.\”traductor\” (
   \”id\” integer not null,
   \”catala\” varchar(100),
   \”castella\” varchar(100),
)
”’


Faig insert de dades amb diferents mètodes:

– fent servir la sintaxi amb ”’
sql.execute ”’
insert into public.\”traductor\”(\”id\”,\”catala\”,\”castella\”) values (1, ‘catala 1′,’castella 1’) 
”’
println “inserta fila 1”

–  fent servir la sintaxi de preparedStatement de JDBC
def params = [2, ‘catala 2’, ‘castella 2’]
sql.execute(“insert into public.\”traductor\” (\”id\”, \”catala\”, \”castella\”) values (?, ?, ?)”, params)
println “inserta fila 2”


params = [3, ‘catala 3’, ‘castella 3’]
sql.execute(“insert into public.\”traductor\” (\”id\”, \”catala\”, \”castella\”) values (?, ?, ?)”, params)
println “inserta fila 3”

– fent servir la sintaxi GString
def map = [id:4, catala:’català 4′, castella:’castellà 4′]
sql.execute(“insert into public.\”traductor\” (\”id\”, \”catala\”, \”castella\”) values ($map.id, $map.catala, $map.castella)”)
println “inserta fila 4”

Visualitzo els resultats amb eachRow, ara fent servir el paràmetre fila a la closure, en comptes d’it.

sql.eachRow(“select * from public.\”traductor\””) {fila ->
    println “id = $fila.id; català = $fila.catala; castellà = $fila.castella”
}

Després d’aquestes accions, tinc les dues taules amb dades, com puc veure amb l’OOo/LO

He fet inserts, evidentment també es poden fer update, delete… fent servir execute, o millor  executeUpdate, que em retorna una llista de les files afectades.

eachRow admet paginació. He afegit algunes dades més a la taula traductor i la visualitzo, primer tota i després mostrant només quatre files a partir de la fila tres:

println “mostra la taula \”traductor\””
sql.eachRow(“select * from public.\”traductor\””) {fila ->
    println “id = $fila.id; català = $fila.catala; castellà = $fila.castella”
}


println “mostra 4 files de la taula \”traductor\” a partir de la fila 3″
sql.eachRow(“select * from public.\”traductor\””, 3, 4 ) {fila ->
    println “id = $fila.id; català = $fila.catala; castellà = $fila.castella”
}

El resultat és:
mostra la taula “traductor”

id = 1; català = catala 1; castellà = castella 1
id = 2; català = catala 2; castellà = castella 2
id = 3; català = catala 3; castellà = castella 3
id = 4; català = català 4; castellà = castellà 4
id = 5; català = catala 5; castellà = castella 5
id = 6; català = catala 6; castellà = castella 6
id = 7; català = catala 7; castellà = castella 7
id = 8; català = català 8; castellà = castellà 8
mostra 4 files de la taula “traductor” a partir de la fila 3
id = 3; català = catala 3; castellà = castella 3
id = 4; català = català 4; castellà = castellà 4
id = 5; català = catala 5; castellà = castella 5
id = 6; català = catala 6; castellà = castella 6

La classe groovy.sql.Sql té altres mètodes que també permeten augmentar la productivitat del desenvolupament, que s’afegeixen al fet que els scripts en Groovy no requereixen compilació. 
groovy.sql.Sql també té mètodes que retornen ResultSet de jdbc, permetent el tractament típic de Java.
El package groovy.sql també inclou la classe  DataSet que deriva de groovy.sql.Sql i que permet fer queries a la base de dades fent servir operadors i noms de camps de Groovy en comptes de crides a API JDBC i noms de taules i columnes. Un petit exemple:
Primer de tot, cal importar DataSet
import groovy.sql.DataSet
i ja el puc fer servir:
primer mostro el contingut de la taula amb eachRow d’Sql i després fa la query amb DataSet
println “mostra la taula \”traductor\””
sql.eachRow(“select * from public.\”traductor\””) {fila ->
    println “id = $fila.id; català = $fila.catala; castellà = $fila.castella”
}
println “mostra valors amb id entre 3 i 7 fent servir la classe DataSet”
def traduccions = sql.dataSet(“public.\”traductor\””)
def filtrat = traduccions.findAll {it.”\”id\”” >= 3 && it.”\”id\”” <= 7 }
filtrat.each { fila ->
    println “id = $fila.id; català = $fila.catala; castellà = $fila.castella”
}
El resultat és el següent
mostra la taula “traductor”
id = 1; català = catala 1; castellà = castella 1
id = 2; català = catala 2; castellà = castella 2
id = 3; català = catala 3; castellà = castella 3
id = 4; català = català 4; castellà = castellà 4
id = 5; català = catala 5; castellà = castella 5
id = 6; català = catala 6; castellà = castella 6
id = 7; català = catala 7; castellà = castella 7
id = 8; català = català 8; castellà = castellà 8

mostra valors amb id entre 3 i 7 fent servir la classe DataSet
id = 3; català = catala 3; castellà = castella 3
id = 4; català = català 4; castellà = castellà 4
id = 5; català = catala 5; castellà = castella 5
id = 6; català = catala 6; castellà = castella 6
id = 7; català = catala 7; castellà = castella 7
En aquest exemple les particularitats de HSQLDB pel que fa als noms de taules i columnes entre cometes  no permeten aprofitar la simplificació de fer servir els noms de les columnes com si fossin noms de camps Groovy de l’objecte it. Però, si més no, es permet veure l’ús de l’operador && per a construir el “where” del Dataset
I finalment, per acabar…
// tanca la connexió
sql.close()

Retardador de subtítols SubRip (again bis). Groovy version.

El darrer post va ser la versió BeanShell del retardador [1] [2]. No he pogut resistir-me a fer  la versió amb Groovy.

import java.text.DateFormat
import java.text.SimpleDateFormat

// els arguments estan a la variable args

// verifica que té 3 arguments
if (args.length != 3 ) {
    sUsage = “Usage:\tdelayer filein.srt fileout.srt delay” +
             “\n\texample 1: delayer filein.srt fileout.srt 10000 –> delays 10 seconds” +
             “\n\texample 2: delayer filein.srt fileout.srt -10000 –> advances 10 seconds”

    print “incorrect number of arguments”
    print(sUsage)
    System.exit(1)
}

// l’anterior també es podria fer amb CliBuilder

// calcula el delta
lDelta = Long.parseLong(args[2])

// prepara el SimpleDateFormat
sdf = new SimpleDateFormat(“HH:mm:ss,SSS”)

// obre els fitxers
fFileIn = new File(args[0])
fFileOut = new File(args[1])

// buffer
sFileOut = “”

fFileIn.eachLine { sLine -> 
                sTokens = sLine.split(“–>”)
                if (sTokens.length == 2) {
                    // si té dos elements, és la línia de temps
                              
                    // els “pela”
                    sTempsInici = sTokens[0].trim()
                    sTempsFi = sTokens[1].trim()

                    // els converteix a Date
                    dTempsInici = sdf.parse(sTempsInici)
                    dTempsFi = sdf.parse(sTempsFi)
                              
                    // Els suma el retard
                    dTempsIniciDelayed = new Date(dTempsInici.getTime() + lDelta)
                    dTempsFiDelayed = new Date(dTempsFi.getTime() + lDelta)

                    // converteix els Date a String amb el mateix SimpleDateFormat
                    sTempsIniciDelayed = sdf.format(dTempsIniciDelayed)
                    sTempsFiDelayed = sdf.format(dTempsFiDelayed)

                    // Munta la línia
                    sLine = “${sTempsIniciDelayed} –> ${sTempsFiDelayed}”
    }
               
    sFileOut += “${sLine}\r\n”
}
// escriu el buffer
fFileOut.write(sFileOut)

// no hi ha close . L’objecte File no en té. ni li cal.

// acaba aquí

La versió en Groovy té un aspecte sensiblement diferent de la versió BeanShell. Ara bé, cal dir que per a escriure el codi Groovy he parti del codi BeanShell, que, excepte per la presa d’arguments l’intèrpret Groovy l’ha agafat sense cap més queixa. Diguem que amb el BeanShell ja tenia el 99% del Groovy fet. Per la seva banda, el BeanShell era, pràcticament, Java.

Però, és clar, el codi resultant no tenia l’aspecte d’un script Groovy.  Així que vaig començar a modificar-lo per a seguir un estil de codificació més Groovy.

Primer de tot, el tractament dels arguments és més senzill que amb BeanShell, doncs args ésdisponible directament. Com amb la vesió Python, hauria pogut utilitzar un mòdul per a tractar els arguments de la línia de comandes, el CliBuilder, però ho deixo per a una altre ocasió.

Observar que no he fet servir “;” .

Importació de llibreries Java i ús de SimpleDateFormat per a fer el parseig de ñes cadenes i el formateig dels dates, com amb BeanShell.

Simplificació de la lectura i escriptura dels fitxers amb l’objecte Groovy File. Aquest objecte NO és l’objecte Java File. File de Groovy simplifica el tractament dels fitxers. Fixem-nos com n’hi ha prou amb obrir el fitxer per nom i després, fent us d’una closure,  llegeix línia per línia el fitxer. M’he estalviat els BufferedReader.

Per a cada línia fa el mateix tractament  que amb l’script BeanShell però, en comptes d’escriure línia a línia, el que fa és construir una cadena sFile, un buffer, amb tota la informació del fitxer. En comptes de fer servir l’operador ‘+’ per a concatenar, opta per una solució més Groovyana:

sLine = “${sTempsIniciDelayed} –> ${sTempsFiDelayed}”

Un cop processades totes les línies, escriu el buffer de cop, amb el mètode write de l’objecte File.

És evident la simplificació comparant amb BeanShell, o que l’equivalent amb Java. M’atreviria a dir que, fins i tot, és més simplificat que amb Python.

Però tampoc aniré més enllà. Es tracta d’un script molt senzill i no seria correcte treure’n conclusions sobre la potència dels tres llenguatges. Segur que en totes tres versions, Python, BeanShell, Groovy es podrien buscar simplificacions addicionals. I en tot cas, a l’hora de triar una solució o altre, hi han factors que poden ser molt més determinants que la potència del llenguatge: disponibilitat d’entorns de desenvolupament, coneixements i experiència dels programadors…

Prenguem, doncs, aquest script d’avui només com un petit tast d’un altre aroma del cafè: Groovy, un altre llenguatge per a la JVM.

Groovy, un altre llenguatge per a la JVM

Al darrer post on parlava de BeanShell vaig esmentar Groovy. Però esmentar-lo no és suficient. Groovy es mereix un post. Vostès perdonaran, però fent el joc de paraules, Groovy és meravellós.

Groovy és un llenguatge per a la Java Virtual Machine (JVM). Si la seva sintaxi fos molt diferent a la de Java no tindríem cap dubte en veure’l com quelcom de diferent a la família Java. El cas, però, és que tot programa en Java és també un programa en Groovy.  La inversa no és certa perquè Groovy incorpora canvis i extensions diverses. Groovy pot importar i utilitzar els packages de Java.

Igual com ho fa BeanShell importa un grup de packages java per defecte (els d’ús més habitual).

Moltes de les característiques del llenguatge que vaig comentar per a BeanShell segueixen sent vàlides per a Groovy. Però BeanShell, a diferència de Groovy, està pensat i dissenyat per a ser un llenguatge d’scripting per a Java. Mentre que Groovy, tot i que també pot actuar com llenguatge d’scripting per a Java, és un llenguatge de construcció d’aplicacions.

Ara bé, el mode de funcionament habitual de Groovy és interpretat (realment es fa un compilat JIT a bytecode de Java, però es descarta el bytecode un cop s’ha utilitzat), a diferència del Java que és compilat. Això situa Groovy en el grup de llenguatges com Python, o Perl, o Ruby que es compilen al moment. Tanmateix, amb el JDK de Groovy s’entrega el compilador groovyc que permet compilar a bytecode de Java. És dir que podrem generar fitxers .class i executar-los a una JVM.

Per començar amb Groovy n’hi ha prou amb crear un script amb l’extensió .groovy i executar-lo amb l’aplicació groovy. De cara a depurar-ne el funcionament, però, pot ser útil fer servir l’intèrpret interactiu groovysh. Una versió  gràfica de groovysh és la groovyConsole. El JDK inclou els quatre programes esmentats: groovy, groovyc, groovysh i groovyConsole.

Al cas de Linux Ubuntu, la instal·lació de Groovy es pot realitzar des del Centre de Programari de l’Ubuntu. Convé instal·lar també la documentació, que queda ubicada a /usr/share/doc/groovy-doc. La documentació inclou els javadoc. Per a una introducció al llenguatge, o guies d’usuari cal anar a la web de Groovy, a la secció de documentació.

La forma fàcil d’accedir al llenguatge és partint de Java, si més no per a tots aquells que coneixem aquest llenguatge.

Una classe Java o un programa Java són executables per Groovy sense fer res més. Ara bé, si es vol treure profit de Groovy cal utilitzar-ne les seves característiques, aleshores el que es pot fer és anar despullant el Java de tot allò que és redundant o no necessari en Groovy:

– Els següents packages estan importats per defecte:

  • java.io.*
  • java.lang.*
  • java.math.BigDecimal
  • java.math.BigInteger
  • java.net.*
  • java.util.*
  • groovy.lang.*
  • groovy.util.*

– ‘==’ funciona sempre com em mètode  equals().

– ‘in’ és paraula clau.

– A Java un array es pot declarar com int[] a= {1,2,3}; a Groovy cal fer-ho int[] a= [1,2,3]; és dir amb [].

– Els llaços ‘for’ admeten construccions alternatives.
Per exemple, en Java es pot fer   for (int i=0; i < len; i++) {…}
A Groovy també i,  a més, són possibles les construccions alternatives amb ‘in’
for (i in 0..len-1) {…}
for (i in 0..<len) {…}

i aquesta última que aprofita que a Groovy tot són objectes
len.times {…}
Aquesta última és una diferència important. A Java hi han uns tipus primitius, per exemple int, que en ocasions cal envoltar amb uns wrapper, per exemple, Integer per a poder utilitzar-los com objectes. Igualment, en ocasionsm cal passar de Integer a int. Això és el boxing/unboxing. A Groovy no cal. Tot són objectes. Un número té mètodes, com el mètode times, per exemple.

– El ‘;’ al final de línia és opcional (però recomanable si es vol claredat).

– El ‘return’ de les funcions és opcional (però, també, recomanable per claredat).

– ‘this’ es pot fer servir dins de mètodes estàtics.

– Per defecte, les classes i mètodes són públics.

– Groovy, com BeanShell, o com es fa JavaScript, permet definir objectes a partir de funcions. En realitat, pot fer-ho a partir de blocs. Això és el que s’anomenen closures (o clausures).

– A Java cal posar blocs try-catch, o throws. A Groovy no cal (evidentment, si es produeix una excepció, no serà capturada).

– Groovy només detectara errors com l’ús de mètodes no declarats, o el pas d’arguments de tipus equivocat en temps d’execució. A Java aquesta mena d’errors es detecten en temps de compilació.

Una bona referència per veure  diferències entre Java i Groovy (i també amb Ruby) són aquestes pàgines de A. Sundararajan’s Weblog. No és un llistat exhaustiu de les diferències.

http://blogs.oracle.com/sundararajan/entry/java_groovy_and_j_ruby

http://blogs.oracle.com/sundararajan/entry/java_groovy_and_j_ruby1

Finalment, un altre bon lloc on trobar snippets de Groovy és http://www.groovyexamples.org/

I fins aquí la presentació de Groovy. El següent pas és, doncs, posar a treballar Groovy. El llenguatge és potent. Prou potent com per ser la base de Grails. Grails ve a ser l’equivalent amb Groovy al framework web Ruby on Rails, basat en Ruby. De Grails, com no, caldrà parlar-ne en un proper post.