viernes, 23 de noviembre de 2012

Greach 2.0 - Groovy Spanish Conference

Ya podeis comprar los tickets para la Greach o lo que es lo mismo la Groovy Spanish Conference que se celebrará en Madrid los días 25 y 26 de Enero del 2013.
Ya están confirmados algunos de los speakers que darán charlas como Graeme Rocher, Cedric Champeau, Andres Almiray, Sebastian Blanch, Corinne Krych, Russel Winder, Burt Beckwith, Marcin Erdmann...
El precio de los tickets varía ya que si lo comprais antes del 12 de Diciembre os costará 90 euros y sino el precio es de 120 euros.

Cambio de look en grails.org

Ya podeis echarle un vistazo a la nueva web que nos han dejado los miembros de grails.org

viernes, 26 de octubre de 2012

Lo nuevo de Groovy 2.0

Recordaremos primero lo que llevaba Groovy 1.8:

– Mejores DSLs con command chains
 Una sintaxis mejorada en donde puedes eliminar puntos y paréntesis cuando encadenas varias llamadas a métodos.
– Mejoras en el rendimiento en tiempo de ejecución
– GPars está ya agrupado dentro de la distribución de Groovy
– Mejoras en las Closures
 Anotaciones con closures como parámetros y método memoize() en closures para cachear un número de invocaciones.
– Soporte mejorado para la producción, construcción e impresión de JSON
– Nuevas transformaciones AST
 4 loggers diferentes pueden ser inyectados (@Log, @Commons, @Log4j y @Slf4j) o posiblidad de implementar tu propia estrategia.
import groovy.util.logging.*

@Log
class Car{
  Car() {
     log.info 'Car constructed'
  }
}

def car = new Car()
@Field
@AutoClone y @AutoExternalizable
@Canonical
 @toString: para crear un método toString() a tus tipos.
import groovy.transform.ToString

@ToString
class Person {
  String name
  int age
}

println new Person(name:'Edu', age:31)
// => Person(Edu,31)
 @EqualsAndHashCode: para crear una implemetación por defecto de equals() y hashCode()
import groovy.transform.EqualsAndHashCode

@EqualsAndHashCode
class Coord {
  int x, y
}

def c1 = new Coord(x:20,y:10)
def c2 = new Coord(x:20,y:10)

assert c1==c2
assert c1.hashCode()==c2.hashCode()
 @TupleConstructor: crea un clásico constructor con todas las propiedades
import groovy.transform.TupleConstructor

class Person {
  String name
  int age
}

def person = new Person(name:'Edu', age:31)
assert person.name=='Edu'
assert person.age==31
Controlar la ejecución del código para cuando el código entra en un bucle infinito o consume muchos recursos. Se hace con las 3 nuevas anotaciones: @ThreadInterrupt, @TimeInterrupt y @ConditionalInterrupt.
import groovy.transform.ThreadInterrupt

@ThreadInterrupt
void main(){
  while(true){
    if(Thread.currentThread().isInterrupted())
      throw new InterruptedException()
    //eat lots of CPU
  }
}
@InheritConstructor: para que se pinten todos los constructores heredados
@WithReadLock y @WithWriteLock
@ListenerList

Lo nuevo de Groovy 2.0 (NO INCLUIDO EN GRAILS 2):

– Alineaciones con JDK 7: Proyecto Coin (Binary literals, Underscore en literales y Multicatch) y InvokeDynamic ()
//Ejemplo de Multicatch
  try{
      ...
  }catch(IOException | NullPointerException e){
    //Un bloque para tratar 2 excepciones
  }
– Continuan las mejoras de rendimiento en tiempo de ejecución
– Modularidad (todos los jars de Groovy pesan 6 Mb, con un pequeño core (3 Mb) y un pequeño jar por feature)
 console – jsr-223 – test – docgenerator – jmx – testng – groovydoc – sql – json – groovysh – swing – xml – ant – servlet – bsf – templates.
Dejaremos las dos más importantes features para el final: static type checking y static compilation.

@TypeChecked
Puede estar a nivel de clase o de método. Ayuda a comprobar errores de compilación (no en tiempo de ejecución). No todo el tiempo necesitas las features dinámicas. Comprueba tipos en métodos y variables, asignaciones erroneas, tipos devueltos erroneos y tipos heredados.
import  groovy.transform.TypeChecked

void  method() { ... }   

@TypeChecked  
test() {
  // No se puede encontrar el método  metthhoood()        
  metthhoood()          
  def  name  =  "Eduuuuuu"        
  // Variable  naamme  no está declarada       
  println  naamme 
}
La anotación @TypeChecked funciona en tiempo de compilación y no cambia el comportamiento del código. La mayoría de las features dinámicas no pueden ser @TypeChecked. Sin embargo, la metaprogramación funciona en tiempo de compilación.

@CompileStatic
Esto quiere decir que todo el código se compila estáticamente, es decir genera el mismo byte code que si lo compilásemos con javac.
Ventajas que consigues:
- Tipado seguro
- Código más rápido (casi a la altura de Java)
- Código inmune
- Byte code generado más pequeño
Desventajas
- Pierdes las features dinámicas
- Pierdes el método dispatch
import  groovy.transform.CompileStatic  

@CompileStatic
test() { ... }

jueves, 25 de octubre de 2012

Diferencia entre el método addTo y el operador << (leftShift)

Sabemos que cuando creas una clase de dominio en Grails con una relación hasMany, automáticamente te crearán dos métodos para añadir o borrar instancias a la relación (addTo... y removeFrom...)
class Book {
  static hasMany = [authors: Author]

}
Para añadir una instancia author dentro de Book habría dos formas de hacerlo,
def author = Author.get(1)
def book = Book.get(3)

book.addToAuthors(author) //La colección authors ya está inicializada
ó
book.authors << author //La colección tiene que estar inicializada antes con book.authors = []

miércoles, 24 de octubre de 2012

Múltiples datasources

En Grails 2 uno de los plugins que ya vienen instalados es el Multiple Datasources Plugin. Muy interesante si tienes más de una BBDD a la que atacar.
Por defecto todas las clases de dominio atacan a la misma BBDD configurada en tu Config.groovy,
dataSource {
    pooled = true
    driverClassName = "org.h2.Driver"
    username = "sa"
    password = ""
}
hibernate {
    cache.use_second_level_cache = true
    cache.use_query_cache = true
    cache.provider_class = 'net.sf.ehcache.hibernate.EhCacheProvider'
}

environments {
  development {
    dataSource {
      dbCreate = "create-drop" 
      url = "jdbc:h2:mem:devDb" 
    } 
  } 
  test {
    dataSource {
      dbCreate = "update"
      url = "jdbc:h2:mem:testDb"
    }
  }
  production {
    dataSource {
      dbCreate = "update"
      url = "jdbc:h2:prodDb" 
    }
  } 
}

Si quieres configurar otra BBDD, la tendrás que definir al mismo nivel que dataSource, sólo que cambiándole el nombre, en este caso dataSource_lookup,
dataSource {
    pooled = true
    driverClassName = "org.h2.Driver"
    username = "sa"
    password = ""
}
hibernate {
    cache.use_second_level_cache = true
    cache.use_query_cache = true
    cache.provider_class = 'net.sf.ehcache.hibernate.EhCacheProvider'
}

environments {
  development {
    dataSource {
      dbCreate = "create-drop" 
      url = "jdbc:h2:mem:devDb" 
    }
    dataSource_lookup {
            dialect = org.hibernate.dialect.MySQLInnoDBDialect
            driverClassName = 'com.mysql.jdbc.Driver'
            username = 'lookup'
            password = 'secret'
            url = 'jdbc:mysql://localhost/lookup'
            dbCreate = 'update'
    } 
  } 
  test {
    dataSource {
      dbCreate = "update"
      url = "jdbc:h2:mem:testDb"
    }
  }
  production {
    dataSource {
      dbCreate = "update"
      url = "jdbc:h2:prodDb" 
    }
  } 
}

El siguiente paso sería configurar las clases de dominio. Habría varias posibilidades:
- Que ataque a la nueva BBDD
class ZipCode {

  String code

  static mapping = { datasource 'lookup' } 
}
- Que ataque a varias BBDD
class ZipCode {

  String code

  static mapping = { datasources(['lookup', 'auditing']) } 
}
- Que ataque a varias BBDD, entre ellas la de por defecto
class ZipCode {

  String code

  static mapping = { datasources(['lookup', 'DEFAULT']) } 
}
- Que ataque a todas las BBDD
class ZipCode {

  String code

  static mapping = { datasource 'ALL' } 
}

Luego a la hora de hacer save, delete o cualquier método GORM, tendremos que construirlo con un namespace para decirle a qué BBDD tiene que ir.
def zipCode = ZipCode.auditing.get(42)
…
zipCode.auditing.save()

En el ejemplo anterior, le estaremos diciendo que la instancia de la clase ZipCode tiene que hacer el get y el save en la BBDD "auditing".

martes, 23 de octubre de 2012

El argumento "failOnError"

A veces cuando hacemos un save de una clase de dominio, no sabemos por qué no se han persistido los datos en la BBDD. La mayoría de las veces es porque se nos olvida que todas las clases de dominio tienen una constraint implícita con un nullable: false.
def book = new Book(params)
if (!book.save()) {
    // Save falla!
    ...
}
Para solucionarlo y ver más claro donde ha fallado podemos utilizar el argumento "failOnError". De esta manera cuando haya un error en la validación, nos lanzará una excepción para verlo más claramente.
book.save(failOnError: true)
También se puede definir de forma general para que lo haga con todos los "save" lanzados en la aplicación, configurándolo en nuestro grails-app/conf/Config.groovy,
grails.gorm.failOnError = true
También podemos presentar los errores de la siguiente manera,
def book = new Book(params)
if (!book.save() && book.hasErrors()) {
    // Save falla! Presenta los errores.
    book.errors.allErrors.each{ println it }
    ...
}

lunes, 22 de octubre de 2012

Inyectando beans adicionales

Por convención de Grails, todos los servicios metidos en nuestra carpeta "services" serán inyectados automáticamente por el framework ayudándose de las bondades de Spring.
Pero habrá otros casos en donde definamos un bean en src/groovy o en src/java que vayamos a tener que utilizar en cualquiera de nuestros artefactos más adelante.
Se puede inyectar de dos maneras: usando Spring Bean DSL o por XML.
- Por Spring Bean DSL: modificando el archivo grails-app/conf/spring/resources.groovy y añadiendo el bean dentro de la clousure beans.
import my.company.MyBeanImpl

beans = {
 myBean(MyBeanImpl) { 
   someProperty = 42 
   otherProperty = "blue" 
 } 
}
- Usando XML: modificando el archivo grails-app/conf/spring/resources.xml (lo tienes que crear tu a mano primero) y añadiendo el bean como lo definirías en el applicationContext.xml con Spring.
<bean id="myBean" class="my.company.MyBeanImpl">
    <property name="someProperty" value="42" />
    <property name="otherProperty" value="blue" />
</bean>
Una vez configurado, el bean estará disponible para ser inyectado en cualquiera de los artefactos de Grails, así como en clases que soportan la inyección de depedencias (como puede ser Bootstrap o test de integración)
class ExampleController {

  def myBean … 

}
NOTA: Si defines un bean en el fichero resources.groovy con el mismo nombre que uno que se haya configurado previamente por Grails o por un plugin instalado, tu bean reemplazará al bean previamente registrado. Esto es una forma conveniente de customizar el comportamiento sin recurrir a editar el código del plugin u otra clase que pudiera afectar a la mantenibilidad de la aplicación.

lunes, 8 de octubre de 2012

Consumir servicios web con el Plugin WS-Client

En la entrada anterior veíamos cómo exponer servicios web a través del Plugin CXF de Grails de una manera rápida y sencilla. Ahora nos toca ver cómo consumir un servicio web de un tercero.
Para consumir servicios web, usaremos el Plugin WS-Client basado en GroovyWS.
Lo primero de todo instalaremos el plugin añadiendo esta linea en tu BuildConfig,
compile ":ws-client:1.0"

Ahora sólo tendremos que crear un controlador para consumir ese servicio web con la url del WSDL donde esté publicado.
import org.grails.plugins.wsclient.service.WebService

class ClienteController {
    WebService webService

    def index = {
        def wsdlUrl = "http://ejemplo.com/services/pruebas?wsdl"
        def proxy = webService.getClient(wsdlUrl)
        
        String resul = proxy.saluda("Eylen")
        ...
    }
}
En este ejemplo sólo se ejecuta el método "saluda" del WSDL donde se construye un XML de entrada con un sólo nivel de tipo String y nos devuelve un XML de salida con un sólo nivel también de tipo String.
Habrá XML de salida muchos más complicados que éste, donde habrá que ir navegando por los diferentes niveles consultando la información.
import org.grails.plugins.wsclient.service.WebService

class ClienteController {
    WebService webService

    def index = {
        def wsdlUrl = "http://ejemplo.com/services/pruebas?wsdl"
        def proxy = webService.getClient(wsdlUrl)
        
        def libros = proxy.getLibros()
        libros.libro.each{
           println it.campo
        }
        ...
    }
}
También se puede dar la situación en la que tenemos que construir un XML de entrada con más niveles. Esto se hace construyendo proxies. Tened cuidado con los namespace de los WSDL que puede dar más de un dolor de cabeza. Y también prestad atención si la entrada del WSDL es de tipo String o es una lista.
import org.grails.plugins.wsclient.service.WebService

class ClienteController {
    WebService webService

    def index = {
        def wsdlUrl = "http://ejemplo.com/services/pruebas?wsdl"
        def proxy = webService.getClient(wsdlUrl)

        def libro = proxy.create("defaultnamespace.Libro")

        libro.titulo= "Groovy in Action"
        libro.autor = "Dierk"
        libro.isbn = "123"

        //Detalles es una lista!!!
        def detalle1 = proxy.create("defaultnamespace.Detalles")       
        detalle1.paginas = "150"
        detalle1.indice = "S" 
        libro.detalles << detalle1

        proxy.addLibro(libro)
        ...
    }
}
Para poder debuguear en todos estas construcciones, es mejor que tengais puesto en el Config,
log4j = {
    …
    debug  'org.apache.cxf'
}
Como nota final decir que si teneis instalados en el mismo proyecto los plugins ws-client y cxf puede haber conflictos con la librería cxf. Se recomienda instalar primero el CXF y luego el WS-CLIENT y eliminar de ésta última el jar cxf.

Exponer servicios Grails a través de SOAP con CXF

Una de las facilidades de Grails es que tiene plugins para casi todo. Y uno de los mejores a la hora de exponer servicios web de tipo SOAP es el CXF plug-in for Grails. La única dependencia que teneis que añadir a vuestro BuildConfig es,
compile ":cxf:0.9.0"
Con esta línea, cuando arranqueis vuestra aplicación otra vez, te instalará el plugin. La instalación ya te meterá las librerías necesarias de CXF. La última versión que han sacado ya es compatible para Grails 2.
Para configurarlo se puede hacer de dos formas diferentes:

1. Con la propiedad estática "expose" igual a "cxf"
class TestService {

  static expose=['cxf'] 
  static exclude=["ignoredMethod"]

  boolean serviceMethod(YourDomainClass dc){ return true; }

  boolean ignoredMethod() { return "you shouldn't see me" }

}
Si queremos que algún método del servicio sea ignorado y no se exponga lo declararemos con la propiedad estática "exclude" y el nombre del método.
Todos los métodos que no estén en el exclude serán expuestos y serán accesibles para terceros a través de su url "http://127.0.0.1:8080/nombreApp/services/miServicio?wsdl"
Todas las clases de dominio que sean pasadas o devueltas como parámetros en los servicios web deberán llevar la anotación JAXB @XmlAccessorType(XmlAccessType.FIELD). Esto hará que todos los campos públicos y privados sean serializables. Más info de las anotaciones JAXB aquí.
@XmlAccessorType(XmlAccessType.FIELD)
class YourDomainClass {
  ...
}

2. Con la propiedad estática "expose" igual a "cxfjax". Configurado esto, ya podemos utilizar las anotaciones JaxWS para tener más control sobre tu WSDL.
import javax.jws.*

class TestService { static expose=['cxfjax']

  @WebResult(name="addResult") 
  @WebMethod(operationName="add") 
  int add(@WebParam(name="a")int a, @WebParam(name="b")int b) { 
    return a + b 
  } 
}
Con cxfjax, tienes que poner la anotación @WebMethod en el método del servicio que quieres exponer. Con la anotación @WebResult, le decimos el nombre del parámetro que devuelve el servicio en el WSDL y con @WebParam definimos el nombre del parámetro de entrada que está definido en el WSDL.

Anotaciones de JAXWS:
- @WebService indica que una clase Java está implementando un servicio Web o indica que una SEI (Service Endpoint Interface) está implementando una interfaz de servicio Web. Destino de anotación: Type. Parámetros que permite:

name="" -> El nombre de wsdl:portType. El valor por omisión es el nombre sin calificar de la interfaz o clase Java. (String)

targetNamespace="" -> Especifica el espacio de nombres XML o los elementos WSDL y XML generados a partir del servicio Web. El valor por omisión es el espacio de nombres correlacionado a partir del nombre del paquete que contiene el servicio Web. (String)

serviceName="" -> Especifica el nombre de servicio del servicio Web: wsdl:service. El valor por omisión es el nombre sencillo de la clase java más Service. (String)

endpointInterface="" -> Especifica el nombre calificado de la interfaz de punto final de servicio que define el contrato de servicio web abstracto de los servicios. Si se especifica, se utiliza la interfaz de punto final de servicio para determinar el contrato WSDL abstracto. (String)

portName="" -> wsdl:portName. El valor por omisión es WebService.name más Port. (String)

wsdlLocation="" -> Especifica la dirección Web del documento WSDL que define el servicio Web. La dirección Web puede ser relativa o absoluta. (String)

- @WebMethod indica un método que es una operación de servicio Web. Destino de anotación: Method. Parámetros que permite:

operationName="" -> Especifica el nombre de wsdl:operation que coincide con este método. El valor por omisión es el nombre del método Java. (String)

action="" -> Define la acción de esta operación. En los enlaces SOAP, este valor determina el valor de la cabecera SOAPAction. El valor por omisión es el nombre del método Java. (String)

exclude="" -> Especifica si se ha de excluir un método del servicio Web. El valor por omisión es false.(Boolean)

- @Oneway indica un método como una operación unidireccional de servicio Web que sólo tiene un mensaje de entrada y ningún mensaje de salida. Destino de anotación: Method. No hay parámetros.

- @WebParam personaliza la correlación de un parámetro individual con una parte de mensaje de servicio Web y un elemento XML. Destino de anotación: Parameter. Parámetros que permite:

name="" -> El nombre del parámetro. Si la operación es una RPC (Remote Procedure Call) y no se especifica el atributo partName, este es el nombre del atributo wsdl:part que representa el parámetro. Si la operación es de estilo documento o si el parámetro se correlaciona con una cabecera, -name es el nombre local del elemento XML que representa el parámetro. Este atributo es necesario si la operación es de estilo documento, el estilo de parámetro es BARE, y la modalidad OUT o INOUT. (String)

partName="" -> Define el nombre del atributo wsdl:part que representa este parámetro. Sólo se utiliza si la operación es de estilo RPC o si el estilo de documento y el estilo de parámetro es BARE. (String)

targetNamespace="" -> Especifica el espacio de nombres XML del elemento XML del parámetro. Se aplica únicamente a los enlaces de documentos cuando el atributo se correlaciona con un elemento XML. El valor por omisión es el targetNamespace del servicio Web. (String)

mode="" -> El valor representa la dirección en que fluye el parámetro para este método. Los valores válidos son IN, INOUT y OUT. (String)

header="" -> Especifica si el parámetro es una cabecera de mensaje y no el cuerpo de un mensaje. El valor por omisión es false. (Boolean)

- @WebResult personaliza la correlación de un valor de retorno con una parte WSDL o elemento XML. Destino de anotación: Method. Parámetros que permite:

name="" -> Especifica el nombre del valor de retorno como figura en el archivo WSDL y aparece en los mensajes de la comunicación. Para los enlaces RPC, este es el nombre del atributo wsdl:part que representa el valor de retorno. Para los enlaces de documentos, el parámetro -name es el nombre local del elemento XML que representa el valor de retorno. El valor por omisión es return para enlaces RPC y DOCUMENT/WRAPPED. El valor por omisión es el nombre de método más Response para enlaces DOCUMENT/BARE. (String)

targetNamespace="" -> Especifica el espacio de nombres XML para el valor de retorno. Este parámetro sólo se utiliza si la operación es de estilo RPC o si la operación es de estilo DOCUMENT y el estilo de parámetro es BARE. (String)

header="" -> Especifica si el resultado se lleva a cabo en una cabecera. El valor por omisión es false. (Boolean)

partName="" -> Especifica el nombre de la parte para el resultado con las operaciones RPC o DOCUMENT/BARE. El valor por omisión es @WebResult.name. (String)

- @HandlerChain asocia el servicio Web con una cadena de manejadores definida externamente. Destino de anotación: Type. Parámetros que permite:

file="" -> Especifica la ubicación del archivo de la cadena de manejadores. La ubicación del archivo es un java.net.URL absoluto con forrmato externo o una vía de acceso relativa al archivo de clases. (String)

name="" -> Especifica el nombre de la cadena de manejadores en el archivo de configuración. (String)

- @SOAPBinding especifica la correlación del servicio Web en el protocolo de mensajes SOAP. Destino de anotación: Type o Method. Parámetros que permite:

style="" -> Define el estilo de codificación para los mensajes enviados a y desde el servicio Web. Los valores válidos son DOCUMENT y RPC. El valor por omisión es DOCUMENT. (String)

use="" -> Define el formato utilizado para los mensajes enviados a y desde el servicio Web. El valor por omisión es LITERAL. ENCODED no está soportado en Feature Pack for Web Services. (String)

parameterStyle="" -> Determina si los parámetros del método representan todo el cuerpo del mensaje o si los parámetros son elementos envueltos en un elemento de nivel superior con el nombre de la operación. Los valores válidos son WRAPPED o BARE. Sólo puede utilizar el valor BARE con los enlaces de estilo DOCUMENT. El valor por omisión es WRAPPED.(String)

Mas info en Web Services Metadata Annotations (JSR 181)

viernes, 5 de octubre de 2012

Error de sincronización Grails 2.1.1 con Intellij IDEA 11.1.1

En uno de mis proyectos personales, teniendo instalado la version de Grails 2.1.1 me salía el siguiente error cuando añadía una dependencia en el BuildConfig.groovy,
IDEA hook: Grails not found!
| Error java.lang.reflect.InvocationTargetException
| Error
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
| Error
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
| Error
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
| Error
 at java.lang.reflect.Method.invoke(Method.java:601)
| Error
 at org.jetbrains.groovy.grails.rt.Agent$2.run(Agent.java:73)
| Error at java.lang.Thread.run(Thread.java:722)
| Error Caused by: java.lang.NullPointerException
| Error at org.jetbrains.groovy.grails.rt.GrailsSettingsGetter.printSettings(GrailsSettingsGetter.java:62)
Después de volverme loco durante una hora, encontré la solución. Y es que es un bug en la versión 11.1.1 del Intellij IDEA. La solución hacer un upgrade a las versión 11.1.3 y todo solucionado.

jueves, 4 de octubre de 2012

Todos las opciones del database mapping

En esta entrada trataremos de explicar todas las features que tiene Grails 2 para customizar el mapeo con la base de datos.
Primero de todo diremos que todo esto se puede definir en dos sitios:
- De una manera específica en una clase de dominio
 
 (static mapping = { ... })

-De forma general definiendolo en el Config.groovy con
 
grails.gorm.default.mapping = { ... }


1) AUTOIMPORT
Permite importar o no la clase de dominio en las queries HQL. Por defecto está a true, es decir las clases de dominio por defecto son auto-importadas en las queries HQL, así que sólo con poner el nombre ya es suficiente. Si ponemos esta característica a false, para formar una query habrá que incluir su paquete también para poder identificar la clase de dominio. Esto es una buena solución para cuando tienes dos clases de dominio con el mismo nombre en distintos paquetes y te da un org.hibernate.DuplicateMappingException.
static mapping = {
        autoImport false
    }

2) AUTO TIMESTAMP
Habilitar o no el auto Timestamping, es decir, cuando queremos guardar su dateCreated y su lastUpdated de una clase de dominio automáticamente deberemos tenerlo a true. Si lo quieres deshabilitado para guardar tus propias fechas ponerlo a false.
static mapping = {
        autoTimestamp false
    }

3) BATCH SIZE
Customizar cuántos resultados queremos cuando capturamos datos de la BBDD en una carga lazy.
static mapping = {
        batchSize 10
    }

4) CACHE
Habilitar la cache de segundo nivel de Hibernate.
static mapping = {
        cache true
    }

5) CACHE
Configurar el comportamiento en cascada de la asociación.
static hasMany = [books: Book]

static mapping = { books cascade: 'all-delete-orphan' }

Aquí podemos definir uno o varios comportamientos separados por coma. Hay varios tipos: all, merge, save-update, delete, lock, refresh, evict, replicate or all-delete-orphan (sólo para relaciones one-to-many)
GORM por defecto configura la política de cascada save-update.

6) COLUMN
Customiza la definición de una columna.
static mapping = {
        currency column: "currency", sqlType: "char", length: 3
    }

Primero se define el nombre del atributo seguido de los siguientes parámetros a cambiar:
- column - nombre de la columna como String
- sqlType (optional) - tipo SQL
- enumType (optional) - tipo de enumerado para las propiedades Enum. (ordinal o string)
- index (optional) - nombre del índice
- unique (optional) - si es único
- length (optional) - longitud de la columna
- precision (optional) - nº de posiciones de la parte entera de la columna
- scale (optional) - nº de decimales de la columna

7) DATASOURCE
Configura qué datasource está relacionada la clase de dominio.
static mapping = {
      datasource 'lookup'
   }
environments {
    development {
        dataSource {
            dbCreate = "create-drop"
            url = "jdbc:h2:mem:devDb"
        }
        dataSource_lookup {
            dialect = org.hibernate.dialect.MySQLInnoDBDialect
            driverClassName = 'com.mysql.jdbc.Driver'
            username = 'lookup'
            password = 'secret'
            url = 'jdbc:mysql://localhost/lookup'
            dbCreate = 'update'
        }
    }
}
Si una clase de dominio no tiene una configuración de DataSource, tirará por defecto del DataSource configurado en el Config.groovy.

8) DISCRIMINATOR
Customiza el discriminador de columna cuando se mapea una herencia tipo "table-per-subclass".
class PodCast extends Content {
    …
    static mapping = {
        discriminator "audio"
    }
}

9) DYNAMIC INSERT
Para construir dinamicamente queries INSERT.
static mapping = {
        dynamicInsert true
    }

Por defecto Hibernate genera todas las queries al principio y las cachea. Esto ayuda a que el rendimiento para los insert, update y delete queries no tengan que ser generadas dinámicamente en tiempo de ejecución. Sin embargo, hay ciertas circunstancias donde las queries dinámicas son útiles.

10) DYNAMIC UPDATE
Para construir dinamicamente queries UPDATE.
static mapping = {
        dynamicUpdate true
    }

11) FETCH
Configura el comportamiento de carga de datos de una asociación.
static hasMany = [books: Book]

static mapping = { books fetch: 'select' }

Se define con el nombre de las asociación seguida del parámetro:
- fetch - Estrategia a usar. (join o select)
Por defecto GORM asume que la carga de asociaciones está hecha con una SELECT cuando se accede a la asociación. Si prefieres que la asociación sea cargada de modo eager, puedes cambiarlo sobreescribiendo:
static hasMany = [books: Book]

static mapping = { books fetch: 'join' }

12) ID
Customizar la forma en que el identificador es generado para un dominio.
static mapping = {
        id generator: 'hilo',
           params: [table: 'hi_value', column: 'next_value', max_lo: 100]
    }

Se define poniendo el id seguido de los siguientes parámetros:
- generator (optional) - nombre del generador a usar. Puede ser increment, identity, sequence, hilo, seqhilo, uuid, guid, native, assigned, select, foreign o sequence-identity.
- composite (optional) - coge una lista de propiedades a usar para formar la clave compuesta.
- name (optional) - nombre de la propiedad a usar como identificador.
- params (optional) - algún parámetro para pasar a un generador de identificadores definido.
- column (optional) - nombre de la columna para mapear el identificador. El resto de propiedades también está disponible.

13) IGNORE NOT FOUND
Especifica cómo las FKs que referencian a filas no encontradas son manejadas en las relaciones many-to-one.
String title 
Thumbnail thumbnail

static mapping = { thumbnail ignoreNotFound: true }

Por defecto el valor es false, es decir si no encuentra la fila Hibernate lanzará una excepción "org.hibernate.ObjectNotFoundException". Si lo tienes a true, aunque no lo encuentre, lo ignorará. Esto viene bien cuando tienes BBDD corruptas que le van a faltar datos.

14) INDEX COLUMN
Customiza el índice de una colección de tipo Lista o un Mapa.
static hasMany = [matrix: Integer]

static mapping = { matrix indexColumn: [name: "the_matrix", type: Integer] }

Esto se define con el nombre de la asociación seguido de los siguientes parámetros:
- name - nombre de columna como un String
- type (optional) - tipo Hibernate
- sqlType (optional) - tipo SQL
- enumType (optional) - tipo de enumerado para las propiedades Enum. (ordinal o string)
- index (optional) - nombre del índice
- unique (optional) - si es único
- length (optional) - longitud de la columna
- precision (optional) - nº de posiciones de la parte entera de la columna
- scale (optional) - nº de decimales de la columna

15) INSERTABLE / UPDATEABLE
Determina si una propiedad de la BBDD está configurada como insertable/updateable cuando una instancia es persistida.
String title

static belongsTo = [author: Author]

static mapping = { 
              author insertable: false 
              author updateable: false 
}

Se define con el nombre del atributo seguido de insertable/updateable: boolean. Por defecto están a true.

16) JOIN TABLE
Customiza el tipo de join table que se usa para las relaciones unidireccionales de tipo one-to-many, many-to-many y tipos de colección primitivos.
static hasMany = [projects: Project]

static mapping = { 
projects joinTable: [name: 'EMP_PROJ', column: 'PROJECT_ID', key: 'EMPLOYEE_ID'] 
}

Se define con el nombre de la asociación seguido de joinTable y de los siguientes parámetros:
- name - nombre de table
- key (optional) - clave foránea
- column (optional) - columna inversa

17) LAZY
Configura si se usa proxies y carga de datos lazy para las relaciones one-to-one y many-to-one.
static belongsTo = [author: Author]

static mapping = { author lazy: false }

Por defecto, en GORM las asociaciones single-ended son lazy: true es decir cuando se carga una instancia de un dominio, la asociación de ese dominio no es cargada hasta que se accede a ella.
Hibernate crea un proxy dinámico para subclasificar la clase de dominio y sus métodos y propiedades.

18) ORDER
Customizar el orden por defecto de los resultado de las queries.
static mapping = {
        order "desc"
    }

19) SORT
Customizar el atributo de la clase dominio para ordenar los resultados de las queries.
static mapping = {
        sort "releaseDate"
    }

20) TABLE
Customizar el nombre de la BBDD asociada de la clase de dominio.
class Book {
    static mapping = {
        table "book_catalog"
    }
}

También se le puede especificar el schema y el catálogo definiéndolo así,
static mapping = {
    table name: "book_catalog", schema: "dbo", catalog: "CRM"
}

21) TYPE
Para configurar el tipo de Hibernate para una propiedad particular.
String title

static mapping = { title type: "text" }

Esta propiedad viene bien para definir tipos CLOB o TEXT dependiendo del dialecto de la BBDD.

22) VERSION
Se usa para deshabilitar el bloqueo optimista o cambiar la columna que mapea la versión.
static mapping = {
        version false
    }
static mapping = {
        version 'revision_number'
    }

miércoles, 26 de septiembre de 2012

Entendiendo el Plugin Resources

Este plugin representa una nueva forma de declarar tus recursos estáticos (css, js e img) mediante módulos y lincarlos a tus GSP por medio de tags específicas.
Primero, instala el plugin resources. Si tienes instalado Grails 2 ya viene por defecto instalado. Otros plugins muy interesantes para optimizar el rendimiento de tu aplicación serían: zipped-resources, cached-resources, lesscss-resources, gsp-resources, yui-minify-resources, handlebars-resources y page-resources.
Empezaremos declarando los módulos en el fichero "AppResources.groovy" que está en el directorio conf
modules = {
 common {
  resource url:"css/main.css"
  resource url:"js/jquery.js"
 }
}

Aquí le estamos diciendo que declare un módulo llamado "common" con dos recursos: main.css y jquery.js en donde por defecto los css los pondrá en el head y el js al final de la GSP (“defer”) que le definamos. Si queremos que los js los ponga en el head de la GSP, le añadiremos a la linea del resource disposition:"head"
Ahora necesitas meterlo dentro de una GSP. Para hacer esto, necesitas las tags propias del Plugin. Antes de nada, definamos el layout Sitemesh de nuestra aplicación.
<html>
    <head>
        
        
    </head>
    <body>
        
</body> </html>

El primer r:layoutResources servirá para lincar todos los css, js, iconos, etc que tengas en tu <head> y el segundo para renderizar algún resource “defer”, que normalmente es sólo código JavaScript.

Para utilizar un módulo de recursos en una GSP:
<html>
    <head>
        
        
    </head>
    <body>
    …
    </body>
</html>

A veces tienes que controlar el orden de tus recursos ya que dependen de otros para cargarse bien. La regla general de este plugin es que se empiecen a cargar los recursos en orden de definición (es decir de arriba a abajo). También se puede hacer de otra manera, con "dependsOn". Por ejemplo,
modules = {
    customize{
        dependsOn "common"
        resource url:'css/customize.css'
    }
}

Con esto le estamos diciendo que antes de cargar el módulo customize, debe estar cargado el módulo common.
Otra de las tags que pueden ser útiles es la r:script. Esta tag se utiliza para introducir algo de código JS en la GSP.
<html>
    <head>
        
        
        window.alert('Boo!')
    </head>
    <body>
    …
    </body>
</html>

Este alert que hemos definido se cargará con disposition "defer", es decir se cargará cuando se haya cargado toda la página, aunque esté definido en el head.
Si quisiéramos forzar a que este trozo de JS se cargara en el head le tendríamos que modificar la tag con,
<r:script disposition="head" >

Otro tipo de recurso que se puede utilizar en este plugin son las imágenes. Se puede hacer de dos formas: llamándole desde la GSP,
<r:img uri="images/logo.png"/>

o creando un módulo desde AppResources,
modules = {
    images {
        resource url:'images/logo.png',
            attrs:[width:400, height:180, alt:'ACME Corp.'],
            disposition: 'image'
    }
}

lunes, 24 de septiembre de 2012

Chain = encadenar

Las acciones pueden ser encadenadas. El encadenamiento permite guardar el model de una acción a otra. Por ejemplo llamamos a sucesivas acciones en este código:
class ExampleChainController {

def first() { chain(action: second, model: [one: 1]) }

def second () { chain(action: third, model: [two: 2]) }

def third() { [three: 3]) } 
}

Al final de la tercera accion tu model sería,
[one: 1, two: 2, three: 3]

El modelo puede ser accedido en el siguiente controller al chain usando el mapa chainModel. Esta propiedad dinámica existe sólo en acciones siguientes a la llamada de algún método chain.
class ChainController {

def nextInChain() { 
def model = chainModel.myModel
 … 
} 
}

Como en el método redirect, puedes pasar también parámetros a un método chain:
chain(action: "action1", model: [one: 1], params: [myparam: "param1"])

Peticiones asíncronas

Grails soporta el procesamiento de peticiones asícronas como característica de la especificación de Servlet 3.0. Para activar la característica necesitas añadir la siguiente líena de código en tu BuildConfig.groovy:
grails.servlet.version = "3.0"

Con esto te aseguras de que tienes la caracterísitica activada en tiempo de compilación.
NOTA: Con el Servlet 3.0 activado, podrás solamente hacer deploy en contenedores que lo soporten como Tomcat 7, etc.
- Renderizar asíncronamente
Puedes renderizar contenido (templates, binary data, etc) de una forma asíncrona por medio de una llamada al método startAsync que devuelve una instancia del AsyncContext de Servlet 3.0. Una vez que tienes una referencia al AsyncContext, puedes usar el método render:
def index() {
    def ctx = startAsync()
    ctx.start {
        new Book(title:"The Stand").save()
        render template:"books", model:[books:Book.list()]
        ctx.complete()
    }
}

Deberías llamar al método complete() para terminar la conexión.

- Reanudar una petición asíncrona
Para reanudar el procesamiento de una petición asíncrona (por ejemplo para delegar a una vista renderizada), se usa el método dispatch de la clase AsyncContext:
def index() {
    def ctx = startAsync()
    ctx.start {
        // do working
        …
        // render view
        ctx.dispatch()
    }
}

viernes, 21 de septiembre de 2012

Interceptores en controladores

A menudo es útil para interceptar procesamientos de cualquier petición, sesión o estado de aplicación. Esto puedes lograrlo con la acción de los interceptores. Actualmente hay dos tipos de interceptores: before and after.

NOTA: Si tu interceptor se aplica a más de un controlador, deberías definir mejor un Filtro. Los filtros pueden aplicarse a múltiples controladores o a URIs sin la necesidad de cambiar la lógica de cada controlador.

- Before Interception
Intercepta procesamientos antes de que la acción se ejecute. Si devuelve false, la acción interceptada no será ejecutada. El interceptor podrá ser definido en todas las acciones de un controlador como lo siguiente:
def beforeInterceptor = {
    println "Tracing action ${actionUri}"
}

El código es declarado en el cuerpo del controlador. Será ejecutado antes que todas las acciones y no interferirá en el procesamiento. Un caso común es una simple autenticación:
def beforeInterceptor = [action: this.&auth, except: 'login']

// defined with private scope, so it's not considered an action 
private auth() { 
if (!session.user) { redirect(action: 'login') return false } }
def login() { // display login page }

El código define un método llamado auth. Un método privado es usado para no exponerlo como acción fuera de todo. El beforeInterceptor define un interceptor que es usado para todas las acciones excepto en la acción login y que ejecuta el método auth. El método auth se referencia usando la sintaxis de puntero de un método Groovy.
Dentro del método detecta si hay un usuario en sesión, si no es así redirige a la acción de login y devuelve un false, causando que la acción interceptada no sea procesada.

- After Interception
Usa la propiedad afterInterceptor para definir un interceptor que es ejecutado después de una acción:
def afterInterceptor = { model ->
    println "Tracing action ${actionUri}"
}

El afterInterceptor toma el resultado del model como argumento y por lo tanto puede manipular el modelo o la respuesta.
Un afterInterceptor puede también modificar el objeto ModelAndView de Spring MVC antes de renderizarlo. E este caso, sería:
def afterInterceptor = { model, modelAndView ->
    println "Current view is ${modelAndView.viewName}"
    if (model.someVar) modelAndView.viewName = "/mycontroller/someotherview"
    println "View is now ${modelAndView.viewName}"
}

Esto permite que la vista sea cambiada basándose en el modelo devuelto por la actual acción. Anotar que el modelAndView puede ser null si la acción que ha sido interceptada llama a un redirect o un render.

Ámbito en los controladores

Al igual que los servicios, nosotros podemos fijar el scope también en los controladores. Por defecto, una instancia del controlador es creada por cada petición. De hecho, todos los controladores por defecto son de ámbito "prototype", es thread-safe que quiere decir que cada petición ocurre en su propio thread.

Puedes cambiar este comportamiento de los controladores sustituyendo por uno de los ámbitos soportados:
- prototype (default): un nuevo controlador será creado por cada petición (recomendado para acciones como propiedades de closure)
- session: un controlador es creado por cada ámbito de sesión
- singleton: solamente una instancia del controlador existe (recomendado para acciones como los métodos)

Para activar uno de los ámbitos, se añade una propiedad estática llamada scope en tu controlador con uno de los ámbitos válidos, cmo por ejemplo,
static scope = "singleton"

Puedes definir la estrategia por defecto en el fichero Config.groovy con la clave grails.controllers.defaultScope, por ejemplo:
grails.controllers.defaultScope = "singleton"

NOTA: Usar los ámbitos en los controladores sabiamente. Por instancia, no recomendamos tener ninguna propiedad en un controlador singleton-scoped ya que serán compartidos por todas las peticiones. Poniendo el ámbito por defecto que no sea prototype también puede dar lugar a comportamientos inesperados si tienes controladores suministrados por plugins instalados ya que se esperan que el ámbito sea prototype.

¿Por qué métodos en lugar de closures?

En versiones anteriores de Grails, las acciones en los controladores eran implementadas con closures. Esto en Grails 2 todavía está soportado, pero la opción preferida es usar métodos.

Elegir métodos en vez de closures tiene algunas ventajas:
- Memoria eficiente
- Permite uso de de controladores en singleton scope
- Puedes sobrescribir acciones de subclases y llamar al método de la superclase con super.actionName()
- Los métodos pueden ser interceptados con mecanismos estandar de proxying, algo que es complicado de hacer con closures ya que tienen campos

Si prefieres la sintaxis con closures o tienes un controlador antiguo creado en una versión anterior de Grails y quieres las ventajas de usar métodos, puedes poner en el BuildConfig.groovy la siguiente linea,

grails.compile.artefacts.closures.convert=true
y en tiempo de compilación las transformaciones AST convertirán tus closures en métodos en el bytecode generado.

NOTA: Si un controlador extiende de otra clase que no está definida dentro del directorio app/controllers/, lo métodos heredados de esa clase no serán conertidos en acciones. Si la intención es exponer esos métodos heredados como acciones de un controlador, los métodos pueden ser sobreescritos en la subclase y el método de la subclase puede invocar al método en la super clase.

jueves, 30 de agosto de 2012

Command objects

Los controladores de Grails y las clases dentro de src/groovy soportan el concepto de los command objects. Un command object es similar a un bean. También se le puede definir validación.
- Declarando command objects
import grails.validation.Validateable

class SampleController {
  ...
}
@Validateable
class LoginCommand {
    String username
    String password

static constraints = { username(blank: false, minSize: 6) password(blank: false, minSize: 6) } 
}
* Como se muestra en el ejemplo, la clase del command object está marcada por la anotación Validateable, donde con esto podrás definir constraints del mismo modo que en las clases de dominio. Otra forma de hacer un command object validable es definirlo sin la anotación y meter una la siguiente línea en tu Config.groovy
grails.validateable.classes = [com.example.LoginCommand]
- Uso del command object
Para usar los command objects en las acciones de los controladores que se quieran utilizar, se le pasará como parámetro dicho command object.
class LoginController {

def login(LoginCommand cmd) { if (cmd.hasErrors()) { redirect(action: 'loginForm') return }

// work with the command object data } }
Antes de que la acción del controlador se ejecute, Grails creará automáticamente una instancia del command object y rellenará sus propiedades con los parámetros de la petición. Si el command object está marcado con @Validateable, entonces se validará con sus contraints.
- Inyección de dependencias dentro de los command objects
@Validateable
class LoginCommand {

def loginService

String username String password

static constraints = { username validator: { val, obj -> obj.loginService.canLogin(obj.username, obj.password) } } }
* Para quien le interese, también se puede crear command objects dentro de servicios. Aquí os dejo el enlace donde lo explican http://jts-blog.com/?p=252

jueves, 9 de agosto de 2012

Diferencia entre get / read / load

A todos se les pasa el ID como identificador.
- El método get carga el objeto de base de datos.
- El método read carga el objeto en modo lectura. En este caso Hibernate no hará dirty checking y el objeto no será persistido.
- El método load carga un proxy para una instancia. Esto no incurre el acceso a base de datos.

Herencia en GORM

GORM soporta la herencia tanto de clases abstractas como de entidades persistentes.
class Content {
 String author
}

class BlogEntry extends Content {
 URL url
}
A nivel de base de datos, Grails por defecto usa la estrategia de mapeo "table-per-hierarchy" con una columna discriminandora llamada "class", así que la clase padre Content y su hijo/s (BlogEntry, etc) compartirán la misma tabla.

La estrategia de mapeo "table-per-hierarchy" tiene un punto débil que es que no puede tener propiedades non-nullable. Una solución podría ser cambiar la estrategia y ponerla a "table-per-subclass".
class Payment {
Integer amount
static mapping = [ tablePerHierarchy : false ]
}
class CreditCardPayment extends Payment {
String cardNumber
}
Sin embargo, el excesivo uso de la herencia y de la estrategia "table-per-subclass" puede penalizar en el rendimiento por el uso de outer join queries. El consejo es usar la herencia pero no abusar de ella sobre todo si es muy profunda.

miércoles, 8 de agosto de 2012

Data binding en los argumentos de la acción

En Grails 2 una de las caraterísticas era el databinding en los argumentos de la acción de un Controlador.
def save (float width, float height) {
...
}
Pero ahora podemos cambiar el nombre de los argumentos para poder utilizarlos en la acción con la anotación @RequestParameter de Spring 3 (ej. para conectar con una API externa).
def save (@RequestParameter('w') float width,  @RequestParameter('h') float height){
...
}
* Ahora el campo width se inicializá con params.w y height con params.h

Entendiendo ignoreNotFound

Sirve para subsanar el error de una referencia perdida de una FK (foreign key borrada) en una base de datos en una relación many-to-one.
Cuando los datos en una base de datos están corruptos y una FK no existe, Hibernate nos dará un mensaje de "No row with the given identifier exists".
Una forma de solucionarlo es poner ignoreNotFound a true para que Hibernate trate la columna perdida como una asociación nula.

*Hay que tener cuidado porque cuando hagamos el save() nos puede dar una excepción por "Missing reference".

Nuevo método params.date()

Hay un nuevo método en Grails 2 para resolver el problema de parsear fechas cuando vienen en el params.
def date = params.date('myDate')
ó
def date = params.date('myDate','dd/MM/yyyy')

Evitar la doble petición

Esto se hace para evitar que se de más de un click por el usuario a la hora de mandar un formulario.
Se evita utlizando withForm
withForm {
//primera petición
}.invalidToken {
//siguientes peticiones
}
A esto hay que añadirle un token en la tag de form de tu GSP.
<g:form useToken="true" ...>

Nueva constraint bindable

En Grails 2 se ha introducido una nueva constraint llamada bindable.
Se trata de una constraint que se definirá en la clase de dominio donde le diremos a la propiedad que la tenga si es bindable o no, es decir, que cuando hacemos:
instancia.properties = params 
ó
instancia = new Dominio(params)
NO se hará un set de su propiedad.

martes, 7 de agosto de 2012

Tipos de Transacciones

En Hibernate hay dos tipos de transacciones:
  • Declarativas -> son las más sencillas. El usuario no necesita tener el control. 
class AuthorService {
static transactional = true
...
}

ó

class AuthorService {
static transactional = false
@Transactional
void method1(){
...
}
void method2(){
... 
}
}
  • Programáticas -> el usuario necesita mayor control sobre ella.
Author.withTransaction { status->
...
}

Cacheo de Hibernate

Hay tres niveles en Hibernate:
  • Nivel 1 -> se encarga de cachear objetos dentro de una transacción. Activado por defecto en Grails. 
  • Nivel 2 -> se encarga de cachear objetos fuera de las transacciones. Se puede activar de dos formas: 
- De forma general en el archivo DataSource.groovy
hibernate {
          cache.use_second_level_cache=true
          }
- En la clase dominio con
static mapping = {
  cache: true
}
  • Cacheo de queries -> se puede activar también de dos formas:
- En el DataSource.groovy
hibernate {
           cache.use_query_cache=true
          }
- En una query
def person = Person.findByFirstName("Fred", [cache: true])

Querying con GORM

Hay varias formas de tirar una query con GORM:
  • Dynamic finders
  • HQL
  • Criteria
  • Where queries (NUEVO EN GRAILS 2)
  • Detached criteria (NUEVO EN GRAILS 2)

Eager vs Lazy

- Las asociaciones en GORM por defecto son lazy (lazy : true).
- El problema es que con estas asociaciones se comete el error de las N+1 queries.
- Se puede cambiar el tipo de estrategia a Eager (lazy : false) pero con unas curiosidades:
  • lazy : false (sólo para relaciones one-to-many)
  • lazy : false, fetch : 'join' (sólo para relaciones one-to-one y many-to-one)
- También se puede hacer el fetching por lotes, tanto con eager como con lazy
  • lazy : false, batchSize : 10
  • batchSize : 10

Entendiendo los borrados y las actualizaciones en cascada

El tipo de cascada lo dará el belongsTo y el tipo de relación:
  • con belongsTo
          one-to-one             ACTUALIZACIONES EN CASCADA
          one-to-many          (desde la clase propietaria hasta su dependencia)
          many-to-many

          one-to-one             BORRADOS EN CASCADA

          one-to-many          (desde la clase propietaria hasta su dependencia)
          many-to-one

  • sin belongsTo         NO HABRA CASCADAS
          * Tendrás que salvar cada objeto manualmente excepto en las relaciones one-to-many que los "saves" serán automáticamente en cascada si una instancia está en una colección hasMany.

belongsTo

Hay dos formas de poner un belongsTo:
  • sin Mapa
static belongsTo = Author
* Hace que la relación tenga cascada simple.
  • con Mapa
static belongsTo = [author : Author]
* Hace que la relación tenga cascada múltiple y que sea bidireccional.

Relaciones

Hay varios tipos de relaciones que se pueden hacer en GORM:
  • one-to-one (también se utiliza hasOne)
UNIDIRECCIONAL
class Face{
  Nose nose
}

class Nose {

}
BIDIRECCIONAL
class Face{
  Nose nose
}

class Nose { 
  static belongsTo = [face : Face]
}
  • one-to-many / many-to-one
UNIDIRECCIONAL
class Author {
  static hasMany = [books: Book]
class Book {
  
}
* Grails mapea este tipo de relación con una join table.
* ORM permite mapear una relación unidireccional con una FK (foreign key) en la tabla de Author.

BIDIRECCIONAL
class Author {
  static hasMany = [books : Book]
}
class Book {
  static belongsTo = [author : Author]
}
* ORM permite mapear esta relación con una FK (foreign key) en la tabla de Author.
  • many-to-many
class Author {
  static hasMany = [books : Book]
}
class Book {
  static belongsTo = Author //Sin mapa [author : Author] 
  static hasMany = [authors : Author]
}
* belongsTo nos define cual es el propietario de la relación.
* ORM permite mapear este tipo de relaciones con una tabla intermedia con los ids de Author y Book.

Lo nuevo de Grails 2.1

Aquí te destacamos lo más nuevo que trae Grails 2.1 con respecto a las versiones 2.0
  • Mejoras en Maven y soporte para build multi módulos.
  • Grails Wrapper.
  • Opción debug.
  • Comando Grails "alias".
  • Cache Plugin.

martes, 6 de marzo de 2012

Lo nuevo de Grails 2.0

Aquí te destacamos lo más nuevo que trae Grails 2.0 con respecto a las versiones 1.3.x
  • ENTORNO DE DESARROLLO
         1. Modo interactivo y mejoras en la consola.
         2. Mejora en el agente de recarga de clases.
         3. Nuevo reporte para test y templates de documentación.
         4. Usa un TOC para los docs del proyecto.
         5. Mejora el reporte de errores y diagnóstico.
         6. Uso de BBDD en memoria H2 en vez de HSQLDB.
         7. Plugin de tracking de uso.
         8. Mejoras en la resolución de dependencias.
  • CORE
         1. Plugins binarios.
         2. Groovy 1.8
         3. Spring 3.1
  • WEB
         1. Las acciones de los controladores son métodos en vez de closures.
         2. Binding de parámetros en los métodos del controlador.
         3. Abstracción de los recursos estáticos (css, img y js).
         4. Características del Servlet 3.0 asíncrono.
         5. Generar link en código .
         6. Generar render en código.
         7. Poder hacer exclusiones en filtros.
         8. Mejoras en el rendimiento.
         9. Scaffolding en HTML5.
        10. JQuery por defecto en vez de Prototype.
        11. Fácil parseo de fechas con "params.date()".
        12. Customizable los formatos de URL.
        13. Web flows con inputs y outputs.
  • PERSISTENCIA
        1. Mayor rendimiento del GORM API.
        2. Detached criterias y Where queries.
        3. Nuevos métodos de findOrCreate y findOrSave.
        4. Herencia abstracta.
        5. Soporte para mútiples DataSources.
        6. Database Migration Plugin.
        7. Database Reverse Engineering Plugin.
        8. Hibernate 3.6
        9. Colecciones de tipo Bag.
  • TESTING
        1. Nueva salida por consola para tests unitarios.
        2. Nueva API para tests unitarios.
        3. GORM para tests unitarios.
        4. Tests unitarios más rápidos y modo interactivo.
        5. Scaffolding para lo tests unitarios.