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.