Fullmenu null

 

30 March 2018

Note
GORM (Groovy o Grails?) Object Relational Mapping, http://gorm.grails.org/, es la librería pertenciente al proyecto Grails que permite el acceso y gestión de bases de datos. En un principio sólo contemplaba Hibernate pero con las últimas versiones permite acceder a bases de datos de grafos como Neo4J, NoSQL como MongoDB, etc manteniendo la misma sintáxis y funcionalidades

En este artículo vamos a desarrollar un Groovy Script simple donde veremos que podemos utilizar todas las funcionalidades de GORM (creación y mantenimiento de las tablas, relaciones entre ellas, persistencia en nuestro modelo de dominio, etc) desarrollando una pequeña biblioteca con libros.

Dependencias

@GrabConfig(systemClassLoader=true)
@Grab('org.slf4j:slf4j-api:1.7.10')
@Grab('org.xerial:sqlite-jdbc:3.21.0')
@Grab('org.grails:grails-datastore-gorm-hibernate5:6.1.6.RELEASE')
@Grab('com.enigmabridge:hibernate4-sqlite-dialect:0.1.2')

import groovy.sql.Sql
import groovy.transform.ToString
import org.grails.orm.hibernate.HibernateDatastore
import grails.gorm.annotation.Entity

Domain

Nuestros objetos de Dominio constan de Biblioteca y Libro donde el primero contiene una relación de los segundos

diag e39236c1a3d75e2c03e3491c50a3cc30

En nuestro script declararemos dos clases @Entity donde indicaremos los atributos y relaciones entre ellas tal como requiere GORM

@Entity
@ToString
class Libro {
    String codigo
    String titulo
    static constraints = {
        codigo unique:true
    }
}

@Entity
@ToString
class Biblioteca{
    String nombre
    static hasMany = [ libros : Libro]
}

Simplemente por anotar nuestros objetos de dominio con @Entity, GORM los recubrirá con toda una gama de funciones y capacidades que nos permitirán persistir estos objetos así como recuperarlos. Por simplificar este script no utiliza -validators_ ni otras funcionalidades de GORM.

Configuración

Puesto que nuestro script va a utilizar Hibernate y una persistencia SQLite debemos configurar dicha librería para ello:

Map getConfiguration(){
    [
            'dataSource.url'			:'jdbc:sqlite:example.db',
            'dataSource.drive'			:'org.sqlite.JDBC',
            'hibernate.hbm2ddl.auto'	: 'update',
            'hibernate.dialect'			:'com.enigmabridge.hibernate.dialect.SQLiteDialect'
    ]
}

HibernateDatastore initDatastore(){
    new HibernateDatastore( configuration, Biblioteca, Libro)
}

Simplemente indicamos la url a utilizar, el dialecto y las clases que queremos que Hibernate gestione (las marcadas como @Entity)

Crear una Biblioteca

void prepareBiblioteca() {
    Biblioteca.withTransaction {

        Biblioteca.list().each{ it.delete() }

        new Biblioteca(nombre: 'Biblioteca Nacional').save()
    }
}

Mediante este método accederemos a la base de datos y borraremos todas las bibliotecas que hubiera (probablemente en tu caso no quieras hacer esto. En el nuestro es para demostrar las funcionalidades de GORM) para posteriormente crear una nueva

Como puedes ver usamos un método withTransaction que NO hemos declarado en nuestra @Entity Biblioteca pero que GORM le ha añadido.

Así mismo GORM nos ha añadido métodos como:

  • list() para recuperar todos los objetos de ese tipo

  • delete() para eliminarlo de la base de datos

  • count() para saber cuantos tenemos

  • save() para persistir un objeto de dominio

  • etc

Añadir Libros

void addLibros(){
    Biblioteca.withTransaction{
        Biblioteca b = Biblioteca.first()
        assert b

        b.addToLibros(codigo:'abc', titulo: 'libro 1')

        b.save()
    }
}

Mediante este método vamos a añadir un libro a la primera biblioteca que exista en la base de datos. Como podemos ver GORM nos ha añadido un método addToLibros a la Biblioteca de tal forma que añadir un Libro y crear la relación de dependencia entre ellos nos es transparente

Consultar la biblioteca

Realizar consultas a la base de datos es realmente sencillo y sobre todo con GORM nos olvidamos de sentencias SELECT:

void list(){
    Biblioteca.withTransaction{
        assert Biblioteca.count()   //(1)

        def list = Biblioteca.list()    //(2)
        list.each{ Biblioteca b->
            println "Biblioteca $b.nombre ($b.id): "
            b.libros.each{ Libro l->    //(3)
                println "\t $l.codigo con titulo $l.titulo"
            }

        }
    }

    Biblioteca.withNewSession{
        Biblioteca b = Biblioteca.findByNombre('Biblioteca Nacional')   //(4)
        assert b
        println "Biblioteca encontrada $b"

        List list = Biblioteca.findAllByNombreLike('Bibli%')    //(5)
        assert list.size()

        List list2 = Biblioteca.withCriteria{   //(6)
            eq 'nombre', 'Biblioteca Nacional'
        }
        assert list2.size()
    }
}
  1. similar a select count(*) from biblioteca

  2. similar a select * from biblioteca . Podemos indicar que en la misma query recupere el detalle de los libros pasando como argumento fetch:[libros:"join"]

  3. similar a select * from libros where biblioteca_id = ?

  4. findByXXX donde XXX puede ser cualquier atributo del objeto de dominio

  5. findAll similar a findBy pero recupera una lista en lugar de un sólo registro

  6. para querys más complejas podemos utilizar una closure withCriteria

Test

initDatastore()

prepareBiblioteca()

addLibros()

println '-'.multiply(20)

list()
println '-'.multiply(20)

Script
//tag::dependencias[]
@GrabConfig(systemClassLoader=true)
@Grab('org.slf4j:slf4j-api:1.7.10')
@Grab('org.xerial:sqlite-jdbc:3.21.0')
@Grab('org.grails:grails-datastore-gorm-hibernate5:6.1.6.RELEASE')
@Grab('com.enigmabridge:hibernate4-sqlite-dialect:0.1.2')

import groovy.sql.Sql
import groovy.transform.ToString
import org.grails.orm.hibernate.HibernateDatastore
import grails.gorm.annotation.Entity
//end::dependencias[]

//tag::domain[]
@Entity
@ToString
class Libro {
    String codigo
    String titulo
    static constraints = {
        codigo unique:true
    }
}

@Entity
@ToString
class Biblioteca{
    String nombre
    static hasMany = [ libros : Libro]
}
//end::domain[]

//tag::config[]
Map getConfiguration(){
    [
            'dataSource.url'			:'jdbc:sqlite:example.db',
            'dataSource.drive'			:'org.sqlite.JDBC',
            'hibernate.hbm2ddl.auto'	: 'update',
            'hibernate.dialect'			:'com.enigmabridge.hibernate.dialect.SQLiteDialect'
    ]
}

HibernateDatastore initDatastore(){
    new HibernateDatastore( configuration, Biblioteca, Libro)
}
//end::config[]

//tag::biblio[]
void prepareBiblioteca() {
    Biblioteca.withTransaction {

        Biblioteca.list().each{ it.delete() }

        new Biblioteca(nombre: 'Biblioteca Nacional').save()
    }
}
//end::biblio[]

//tag::libros[]
void addLibros(){
    Biblioteca.withTransaction{
        Biblioteca b = Biblioteca.first()
        assert b

        b.addToLibros(codigo:'abc', titulo: 'libro 1')

        b.save()
    }
}
//end::libros[]

//tag::list[]
void list(){
    Biblioteca.withTransaction{
        assert Biblioteca.count()   //(1)

        def list = Biblioteca.list()    //(2)
        list.each{ Biblioteca b->
            println "Biblioteca $b.nombre ($b.id): "
            b.libros.each{ Libro l->    //(3)
                println "\t $l.codigo con titulo $l.titulo"
            }

        }
    }

    Biblioteca.withNewSession{
        Biblioteca b = Biblioteca.findByNombre('Biblioteca Nacional')   //(4)
        assert b
        println "Biblioteca encontrada $b"

        List list = Biblioteca.findAllByNombreLike('Bibli%')    //(5)
        assert list.size()

        List list2 = Biblioteca.withCriteria{   //(6)
            eq 'nombre', 'Biblioteca Nacional'
        }
        assert list2.size()
    }
}
//end::list[]

//tag::main[]
initDatastore()

prepareBiblioteca()

addLibros()

println '-'.multiply(20)

list()
println '-'.multiply(20)
//end::main[]