Monthly Archives: January 2011

Grails: aspectos interessantes na configuração de acesso a dados

Em diversas aplicações que trabalhei, o único arquivo de configuração que precisamos alterar é o DataSources.groovy. E sabe o que é mais interessante neste componente do framework? É um arquivo de configuração vivo!

Grails sempre me surpreende, até em sua configuração básica. Porém, antes de mostrar alguns aspectos pouco conhecidos deste arquivo, vou começar pelo básico.

DataSource.groovy: o básico

Localizado em seu projeto Grails no diretório grails-app/conf, este é o arquivo aonde devemos incluir as informações que precisaremos para que possamos acessar a base de dados aonde nossas classes de domínio serão persistidas.

Normalmente o arquivo é composto por três seções: dataSource, hibernate e environments. No momento em que criamos uma aplicação Grails com o comando grails-app, este arquivo é gerado com a aparência abaixo:


dataSource {
    pooled = true
    driverClassName = "org.hsqldb.jdbcDriver"
    username = "sa"
    password = ""
}
hibernate {
    cache.use_second_level_cache = true
    cache.use_query_cache = true
    cache.provider_class = 'net.sf.ehcache.hibernate.EhCacheProvider'
}
// environment specific settings
environments {
    development {
        dataSource {
            dbCreate = "create-drop" // one of 'create', 'create-drop','update'
            url = "jdbc:hsqldb:mem:devDB"
        }
    }
    test {
        dataSource {
            dbCreate = "update"
            url = "jdbc:hsqldb:mem:testDb"
        }
    }
    production {
        dataSource {
            dbCreate = "update"
            url = "jdbc:hsqldb:file:prodDb;shutdown=true"
        }
    }
}

Como pode ser visto, cada uma das seções que mencionei compreende um bloco de código, cuja utilidade básica de cada uma, a grosso modo, é a seguinte:

  • dataSource – Armazena as configurações que são comuns a todos os ambientes de execução (falarei mais sobre os ambientes de execução neste post)
  • hibernate – Contém informações específicas do Hibernate, como por exemplo se desejamos trabalhar com cache, expor comandos SQL, etc. Todas as configurações do Hibernate, que você normalmente incluiria no arquivo hibernate.cfg (ou no Spring) deste ORM entram neste bloco.
  • environments – Uma aplicação Grails comporta o conceito de ambientes de execução. No contexto deste arquivo, um ambiente representa uma configuração específica de acesso ao banco de dados. Por default, temos três ambientes: desenvolvimento (development), testes (test) e produção (production).

Como assim ambientes?

Como mencionei, Grails implementa o conceito de ambientes de execução. É comum que no ciclo de vida de uma aplicação esta possua três tipos de ambiente: aquele no qual a desenvolvemos (development), aquele no qual executamos nossos testes automatizados (test) e, finalmente aquele que realmente importa, que é o ambiente de produção (production).

Na realidade, como já escrevi neste blog anteriormente, podemos inclusive ter mais de três ambientes. É uma maneira interessante de se ter por exemplo uma base de dados apenas para a execução de testes, o que facilita demais a vida do desenvolvedor.

O mais interessante é que os ambientes de execução vão além da “mera” configuração de datasources. Você pode, por exemplo, criar um ambiente de execução adicional que encapsule lógica específica de um cliente seu (se sua empresa trabalha com o desenvolvimento de produtos, fica ai minha dica pra você ;) ).

Reaproveitando código – dataSource

A seção DataSource normalmente é mal compreendida por diversos iniciantes. A grosso modo, no entanto, esta é a parte da sua configuração que você deve usar para incluir os aspectos que serão comuns a todos os seus ambientes. Por exemplo: se todos usarem o MySQL como banco de dados, não é necessário redigitar estes dados em cada um dos ambientes, basta inserir estas configurações nesta seção, tal como no exemplo abaixo:

dataSource {
    pooled = true
    driverClassName = "com.mysql.jdbc.Driver"
}
hibernate {
    cache.use_second_level_cache = true
    cache.use_query_cache = true
    cache.provider_class = 'net.sf.ehcache.hibernate.EhCacheProvider'
}
// environment specific settings
environments {
    development {
        dataSource {
            url = "jdbc:mysql://mysql/desenvolvimento"
            username = "jacob"
            password = "lost"
        }
    }
    test {
        dataSource {
            url = "jdbc:mysql://mysql/tests"
            username = "Benjamin"
            password = "Linus"
        }
    }
    production {
        dataSource {
             url = "jdbc:mysql://mysql/production"
             username = "Jack"
             password = "Shephard"
        }
    }
}

E o dbCreate? (LEIA COM ATENÇÃO!)

A propriedade dbCreate é uma das mais úteis e perigosas do Grails. Nós a usamos para hibernate.hbm2ddl.auto, e pode receber os seguintes valores:

  • create – Cria toda a estrutura de banco de dados pra você no momento em que a aplicação for iniciada, e também apaga todos os dados contidos nas tabelas caso estas já existam.
  • create-drop - Funciona da mesma maneira que create. A diferença é que quando a aplicação é finalizada, todas as tabelas são destruídas.
  • update – Atualiza as tabelas do banco de dados. Caso estas não existam, são criadas. Se novos atributos forem incluídos em suas classes, estes serão refletidos em suas tabelas também. No entanto, algumas mudanças não serão efetuadas. Se você por exemplo tiver renomeado campos, por exemplo, o GORM não saberá como lidar com isto (não ocorrerão erros). É importante notar também que caso atributos sejam excluídos de suas classes de domínio, isto não implicará na remoção de campos nas tabelas relacionadas (ainda bem!).
  • validate – simplesmente verifica se as suas classes de domínio estão de acordo com as tabelas do banco de dados. Nenhuma alteração é feita no banco de dados.

Por medida de segurança, no ambiente de produção você deve simplesmente excluir esta propriedade. Assim não correrá o risco de acidentalmente apagar ou modificar uma estrutura de dados que já esteja sendo usada por seus usuários.

Agora, o aspecto “vivo” da configuração

Já reparou que o arquivo “DataSource.groovy” possui a extensão “.groovy”? Ao contrário da maior parte dos arquivos de configuração, algumas das configurações do Grails são na realidade scripts!

Sendo assim, caso seja necessário, você pode definir o funcionamento do seu ambiente não apenas declarativamente, mas também lógicamente. Este aspecto pode nos mostrar uma nova gama de possibilidades, como por exemplo configurar o seu sistema de acordo com variações do ambiente no qual este se encontra instalado.

Confuso? Então aqui segue um exemplo: suponha que você desenvolva produtos, e que não sabe de antemão qual a url de conexão com o banco de dados. Nesta nossa situação hipotética, a string de conexão com o banco de dados poderia ser uma variável de ambiente. Como você faria?

Bem, aqui segue um exemplo:

...
environments {
        ...
         production {
             url = System.getProperty("url_de_conexao_com_o_banco_de_dados")
         }
        ...
}

Na realidade, você pode incluir qualquer código groovy dentro do seu arquivo de configuração. Sendo assim, você poderia ter algo como o código abaixo também.

...
environments {
        ...
         production {
             def valor = null
             switch (System.getProperty("propriedade_qualquer") {
                  case "a": valor = "coisas da vida"
                  case "b": valor = "coisas da vida louca"
             }
             url = valor
             new pacote.qualquer.ClasseQualquer.executeMetodo()
         }
        ...
}

Como pode ser visto no exemplo, você pode inclusive, apesar de não ser recomendado, executar código de inicialização presente em outras classes dentro do seu ambiente de execução. De qualquer maneira, o simples fato de ser possível incluir lógica em um arquivo de configuração já é uma vantagem fascinante.

Um aspecto interessante a ser levado em consideração é que o DataSource.groovy é executado sequencialmente. Sendo assim, em uma configuração padrão, a seção dataSource é executada primeiro, para logo em seguida virem hibernate e environments. Mas sabe de uma coisa? Seu script pode também ser incluído FORA destes blocos e, ainda mais interessante: a ordem em que são declarados não faz diferença alguma.

Como uso JNDI?

A melhor opção, ao menos na minha opinião é, ao invés de incluir suas configurações de acesso ao BD no DataSources.groovy, usar o JNDI. Assim você transfere a responsabilidade pela configuração do pool e outros aspectos do acesso ao banco de dados para o responsável pela manutenção do seu servidor de aplicações. Além disto, também evita de ficar incluindo senhas e nomes de usuários em um arquivo que qualquer um possa ver. Nestes casos, usamos a propriedade jndiName, tal como no exemplo abaixo:

...
   production {
          jndiName = "meu_nome_jndi"
   }
...

É possível ter apenas um ambiente?

Para finalizar este post, uma possibilidade interessante, mas que, em minha opinião, deve ser evitada a qualquer custo. Suponha que por alguma “razão” sinistra, você não queira ter três ambientes distintos, mas apenas um. Como você faria?

Simples: como mencionei anteriormente, a seção dataSource contém o código que será reaproveitado por todos os seus ambientes, certo? Neste caso, basta excluir a seção enrivonments e incluir toda a sua configuração de acesso nesta seção, tal como no exemplo abaixo:


hibernate {
    cache.use_second_level_cache = true
    cache.use_query_cache = true
    cache.provider_class = 'net.sf.ehcache.hibernate.EhCacheProvider'
}
dataSource {
    pooled = true
    driverClassName = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://souLouco/mesmo"
    username = "maluco"
    password = "meio_burro_também"
}

Como pode ver, é possível, mas por favor, não faça isto ok?

Concluindo

Como diria o tio do Homem-Aranha, “com grandes poderes, vêm grandes responsabilidades”. É exatamente o que ocorre aqui. Apesar do framework te oferecer a possibilidade de tratar o arquivo de configuração de acesso ao BD como um script, e você poder até mesmo subverter o padrão e possuir apenas um ambiente, minha sugestão para você é que evite ao máximo possível estes recursos.

Não porque eles podem danificar seu banco de dados ou tornar sua aplicação um monstro, mas sim porque dificulta o entendimento de novos membros que venham a entrar em sua equipe. Afinal de contas, 99,9% dos programadores esperam o comportamento padrão do Grails, não um “arquivo de configuração vivo”, não é mesmo? Porém, se mesmo assim optar por usar estes recursos, faça o possível para documentar estas peculiaridades do seu sistema ok?

Grails: testando sua aplicação

Sabe esta aplicação linda que você está escrevendo em Grails usando todo o dinamismo que o framework te oferece?  Funciona perfeitamente, e em sua cabeça não aparece uma situação sequer na qual algo possa dar errado, certo?

Como você pode ter certeza de que esta aplicação está funcionando? Só há uma maneira: testando, e da maneira certa, ou seja, com testes automatizados. Não há como evitar, sua aplicação VAI virar um monstrinho abominável se voce não escrever testes automatizados.

Os testes automatizados são a sua rede de segurança (safety net): são eles que te dão segurança no momento da manutenção.

Neste post meu objetivo é expor como tirar proveito do Testing Framework, que é o framework de testes adotado pelo Grails. Porém, antes de começar quero acabar com um mito cretino.

“Eu não tenho tempo para escrever testes!”

Papo furado! Se o programador não escreve testes, como pode saber que sua aplicação está DE FATO funcionando? Debugando e executando a aplicação manualmente. E sabe qual o grande problema com esta prática? Ela consome tempo: muito mais do que a escrita e execução dos testes automatizados. E sabe o que é pior? Ainda é sucetível a erros.

Isto sem mencionar que os testes unitários e de integração acabam se tornando a melhor documentação possível para o seu código, pois descrevem exatamente como espera-se que este funcione.

Lido este argumento sua desculpa para ser irresponsável evaporou.

Testes unitários e de integração: qual a diferença?

Testes unitários levam em consideração o objeto a ser verificado isolado. Não há conexões com bancos de dados, web services ou qualquer outro tipo de componente: a classe deve ser vista como um elemento autista. Como veremos, isto trás algumas dificuldades que o testing framework do Grails nos ajuda a resolver de uma maneira simples usando mock objects.

Testes de integração, por sua vez, levam em consideração, como o próprio nome já diz, a integração do objeto a ser testado com componentes externos, como por exemplo bancos de dados, web services ou outros serviços de natureza diversa. Testes de integração são portanto muito mais caros do ponto de vista computacional, visto que precisamos iniciar a aplicação para que estes possam ser executados.

É muito comum encontrar testes unitários que, na realidade, são de integração. Os mock objects entram em cena portanto como elementos externos “de mentirinha”, que nos permitem tratar o objeto isoladamente. Veremos mais sobre este recurso neste post.

Criando seus testes

Todos os testes unitários se encontram no diretório test/unit (ou integration) presente na raiz do seu projeto Grails. Já reparou que toda vez que você cria uma classe de domínio, controlador ou biblioteca de tags (e outros artefatos), automaticamente são incluidos no diretório test/unit?

Há três maneiras de se criar estes testes:

  1. O Grails os cria automaticamente pra você
  2. Criando a classe de teste manualmente (nesta seção ficará claro como isto é feito)
  3. Usando o comando “grails create-unit-test”

Assim como diversos aspectos do Grails, aqui também devemos nos ater a algumas convenções. Toda classe de teste possui o sufixo “Tests” em seu nome. Sendo assim, os testes unitários para a classe de domínio Usuario, por exemplo, ficariam em test/unit/UsuarioTests.groovy.

Uma classe de teste vazia é simples como a abaixo:


import grails.test.*

class UsuarioTests extends GrailsUnitTestCase {
protected void setUp() {}

protected void tearDown() {}

void testSomething() {}
}

O comando “grails create-unit-test” ou “grails create-integration-test” deve receber o nome do teste unitário ou de integração a ser gerado. Você não precisa incluir o “Tests” no final do arquivo, Grails o incluirá para você.

Testando classes de domínio

Quando lidamos com linguagens dinâmicas como Groovy precisamos lidar com o seguinte problema: como testar uma classe que contém métodos e atributos que só serão injetados em tempo de execução? Funções como save(), validate() ou constraints só funcionam após injetados pelo framework.

Podemos escrever testes de integração. O problema é que leva tempo até a aplicação ser iniciada, o que irá reduzir a sua produtividade. O ideal é podermos executar testes unitários, que por sua própria natureza são ordens de magnitude mais rápidos. A solução para o problema é mockar seus objetos usando o Testing Framework do Grails!

(assim como o GORM é baseado no Hibernate, o Testing Framework é baseado no JUnit que você já conhece (ou deveria conhecer! :D))

Para ilustrar, tenha como base esta classe de domínio:


class Usuario {
String nome
String login

static constraints = {
nome(nullable:false, blank:false, maxSize:128, unique:true)
login(nullable:false, blank:false, maxSize:16, unique:true)
}
}

Nosso teste unitário encontra-se na classe abaixo:


import grails.test.*

class UsuarioTests  extends GrailsUnitTestCase {
protected void setUp() {super.setUp()}

protected void tearDown() {super.tearDown()}

void testConstraints() {
mockDomain Usuario
def usuario = new Usuario()
assertFalse usuario.validate()
def usuario_ok = new Usuario(nome:"Joselino", login: "joca")
assertTrue usuario_ok.validate()
}

void testUnicidade() {
mockDomain Usuario, []
def usuario1 = new Usuario(nome: "Joselino", login: "jose")
usuario1.save()
def usuario2 = new Usuario(nome:"Joselino", login:"joca")
assertFalse usuario2.validate() // já existe um Joselino!

}

}

Repare que interessante: mesmo se tratando de um teste unitário estou testando métodos que só existem em tempo de execução: no caso, o validate. Para isto, uso o método mockDomain, herdado de GrailsUnitTestCase. Este injeta na classe domínio todos os métodos que uma classe de respeito deste tipo deve ter, como por exemplo os métodos de validação, save(), delete(), etc.

Assim é possível testar fácilmente a validação. Mais interessante ainda é o segundo teste: testUnicidade. Repare que passo para o método mockDomain uma lista vazia. Quando evoco o método save(), este objeto é na realidade armazenado naquela lista que passei como parâmetro, simulando assim um banco de dados! Não é legal? Assim posso verificar se a constraint unique está de fato funcionando ou não.

No caso de testes de integração, óbviamente você não precisa do método mockDomain, pois as classes já estão prontas.

Testes unitários para controladores

É muito comum encontrarmos projetos nos quais apenas classes de domínio são testadas. Após ler esta seção, sua desculpa para não testar seus controladores acabou! :D

Para testar suas classes, você deve criar um teste tal como faria normalmente. A diferença é que este teste não extenderá a classe GrailsUnitTestCase, mas sim ControllerUnitTestCase. Nosso controlador de exemplo segue abaixo. Observe que ele só tem uma action.


class BoboController {

def bobo = {

if (! params.valor) {
redirect(action:"outra", controller:"outro")
                return
}
switch (params.valor) {
case "boo":
render "Não sou bobo não"
break
case "bonito"
return [resposta:"sou lindo mesmo!"]
case "nada"
throw new Exception("Nada???")
}
}

}

Escrevi alguns testes para esta classe que ilustra bem o que podemos fazer com este framework:

import grails.test.*

class BoboControllerTests extends ControllerUnitTestCase {
    protected void setUp() {
        super.setUp()
    }

    protected void tearDown() {
        super.tearDown()
    }

    void testRedirect() {
		controller.bobo()
		assertEquals "outra", redirectArgs.action
		assertEquals "outro", redirectArgs.controller
    }

	void testRender() {
		mockParams.valor = "bobo"
		controller.bobo()
		assertEquals mockResponse.contentAsString, "Não sou bobo não"
	}

	void testModel() {
		mockParams.valor = "bonito"
		def model = controller.bobo()
		assertEquals model.resposta, "sou lindo mesmo!"
	}

}

Nosso primeiro teste é o de redirecionamento. Observe o método testRedirect(). A classe ControllerUnitTestCase possui um atributo chamado chamado controller, que simula o nosso controlador (o controlador é identificado por default pelo nome que demos à classe do nosso teste).  Chamando nossa action sem nenhum parametro, esperamos, pelo código que expus acima, que sejamos redirecionados para a action “outra” do controller “outro”.

Entra em cena então outro atributo de nossa classe: redirectArgs. Ele normalmente possui 3 parâmetros: controller, action e model. Assim podemos ver se o nosso controlador está se comportando como esperavamos inicialmente.

Se quisermos testar o funcionamento de nosso controlador passando-lhe parâmetros, usamos o atributo mockParams, que funciona exatamente como o params que estamos acostumados a trabalhar. Primeiro devemos incluir os valores neste atributo e em seguida executar nossa action para ver o resultado. Em testeRender podemos ver um exemplo de como verificar se o texto renderizado pelo nosso controlador foi o que esperávamos receber. Para isto, usamos o atributo mockResponse.

Finalmente, inclui também um teste para o valor de retorno de nosso controlador. Observe o método testModel. Basta executar nossa action como se fosse uma função convencional. O restante do código é similar ao que escreveriamos usando um teste unitário convencional.

Há mais alguns atributos que merecem ser mencionados: são estes:

  • mockRequest: usado para simular uma instância de HttpServletRequest
  • mockSession: usado para simular uma sessão (você pode, por exemplo, verificar se seu controller alterou ou não sua sessão após a execução de uma action, o que é MUITO útil)
  • mockFlash: usado para simular o contexto flash. Muito útil para verificar mensagens de alerta que precisam ser enviadas ao usuário, por exemplo.

Executando os testes

Com sua aplicação funcionando, o próximo passo é executar seus testes. Para isto, você usa o comando “grails test-app”, que irá primeiro executar todos os seus testes.

Caso queira executar apenas alguns testes, basta passar como parâmetro os nomes dos testes excluindo o sufixo “Tests”, como por exemplo

grails test-app BoboController

Para executar apenas testes unitários, execute

grails test-app unit

e para executar apenas os testes de integração, execute

grails test-app integration

Executados os seus testes, será criado o diretório target/test-reports em seu projeto, contendo o relatório de execução dos seus testes. Caso esteja usando Grails 1.1, estes relatórios irão estar em test/test-reports.

Concluindo

Um programador que não escreve testes automatizados é como um cirurgião que não lava as mãos antes de uma cirurgia (não me lembro aonde li isto). Com Grails, a escrita dos testes, como pode ser visto, é um processo simples e direto, que irá lhe garantir noites de sono tranquilas após ter entregue seus sistemas.

Agora, se mesmo assim você ainda não quer escrever seus testes, bem: sinto muito, você deve procurar outro ramo se quiser manter um nível mínimo de saúde. :D

Grails: entendendo as validações (constraints)

No meu último post falei sobre alguns detalhes poucos conhecidos das buscas por critérios. Agora chegou a hora de expor algumas coisas bem legais sobre as validações, aka constraints, oferecidas pelo framework.

O básico

As constraints nos permitem definir declarativamente as regras de validação que devemos aplicar às nossas classes de domínio. São muito similares ao projeto “Hibernate Validator”. A diferença é que ao invés de usarmos anotações, iremos usar uma DSL interna definida pelo framework. Assim como no caso da busca por critérios, a DSL usada é baseada no recurso builder presente na linguagem Groovy.

Para declararmos as regras de validação, devemos incluir um bloco estático na nossa classe de domínio chamado constraints, tal como no exemplo abaixo:


class DominioBonito {
String nome
Integer codigo
String email

static constraints = {
nome (nullable:false, blank:false, unique:true)
codigo(min:1)
email(email:true)
}
}

A sintaxe é muito simples: em cada linha você deverá incluir o nome do campo a ser validado, seguido de um par de parênteses contendo as regras a ser aplicadas. Cada regra recebe um ou mais parâmetros, que variam de acordo com a sua implementação. Não se assuste com isto, pois estes atributos se tornarão familiares a você com o tempo.

Todas as constraints default do Grails podem ser acessadas neste link: http://grails.org/doc/latest/ref/Constraints/Usage.html (salve-o em seus bookmarks para referência, pois é uma mão na roda)

Agora que o básico já foi dito, podemos ver detalhes menos conhecidos deste recurso.

Constraints e scaffolding

Eis um detalhe das constraints que não acho bacana: a sua presença influencia o scaffolding. Caso você queira definir a ordem em que os atributos de suas classes de domínio são expostos por este recurso, tudo o que você deve fazer é alterar a ordem na qual os atributos são definidos no bloco constraints da sua classe de domínio.

Há inclusive algumas constraints cujo objetivo é justamente definir regras para o scaffolding. São elas:

display (true ou false): define se o campo vai aparecer ou não nas páginas geradas pelo scaffolding
editable (true ou false): define se o campo será editável no scaffolding ou não
format (string): no caso de datas e números, define qual o formato no qual estes serão expostos, digitados pelo usuário (útil d+!)
password (true ou false): se você definir como true, será exposto um input do tipo password na sua interface gráfica
widget (string): define qual widget deverá ser renderizado. Você deve apenas fornecer qual o nome da tag usada, como por exemplo textarea

Dica: evite este recurso. A classe de domínio deve ser a parte de negócio da sua aplicação, não a responsável pela visualização.

Constraints e GORM

As constraints também influenciam o funcionamento do GORM. No caso da criação das tabelas do banco de dados estar sendo executada pelo framework, as constraints são usadas para definir o tamanho dos campos, assim como o fato de aceitarem ou não valores nulos. Como exemplos, posso citar as seguintes constraints:

maxSize: no caso de campos do tipo varchar ou char, define qual o seu tamanho
nullable: define se o campo pode aceitar valores nulos ou não
unique: cria um índice de unicidade para a coluna relacionada

No caso de maxSize, caso não seja incluida, será usado como valor default de tamanho para campos textuais o padrão do SGBD que, normalmente, é 255 caracteres.

Constraints customizadas

Ok, Grails te oferece uma série de constraints úteis, mas e se você quiser criar uma personalizada? Por exemplo: no cadastro de usuários, ter uma classe de domínio na qual seja verificada a igualdade das senhas digitadas. Como você faz isto? Neste caso, você usa a constraint validator. Veja o exemplo abaixo:


class Usuario {

String nome
String login
String senha1
String senha2

static constraints = {
senha2(validator:  {valor, objeto ->
objeto.properties['senha2'] == valor
})
}

}

A constraint valiator neste caso recebe como valor uma closure que deverá possuir dois parâmetros, obrigatóriamente nesta ordem: valor, objeto. O primeiro representa o valor a ser validado, e o segundo o objeto a ser verificado que, no caso, é a sua instância da classse de domínio a ser validada. O objeto precisa ser passado como parâmetro porque as constraints são um bloco estático. Caso contrário, não haveria como usar este recurso em um ambiente concorrente como aplicações web.

Se o valor da constraint é true, é sinal de que o teste passou. No entanto, esta constraint também pode retornar uma string. No caso, esta string representa uma mensagem de erro, que é definida nos arquivos de mensagens do Grails, presentes no diretório grails-app/i18n. Eu poderia reescrever o exemplo acima da seguinte maneira portanto:


class Usuario {

String nome
String login
String senha1
String senha2

static constraints = {
 senha2(validator:  {valor, objeto ->
if (objeto.properties['senha2'] == valor)
return true
else
return "erro.senhas.diferentes"
})
}

}

E esta mensagem seria exposta na minha camada de visualização tirando proveito do recurso de internacionalização do Grails.

Você também pode usar esta constraint passando como parâmetro uma closure que possua um único parâmetro, tal como neste exemplo:


class Testolina {
Integer valor
static constraints = {
valor(validator: {it % 2 == 0}
}
}

Neste caso, apenas o valor é verificado, que deverá ser um número par.

Constraints globais e compartilhadas

Um recurso muito útil e pouco comentado do Grails é a possibilidade de se criar regras de validação globais e compartilhadas. Imagine a seguinte situação: no seu projeto, por default, todos os atributos devem ser não nulos por default. Não é chato ter de repetir esta regra em todas as suas classes de domínio?

Você inclui uma regra de validação global no arquivo grails-app/conf/Config.groovy. Basta adicionar um código similar ao abaixo:


grails.gorm.default.constraints = {
'*'(nullable:false)
}

O * diz que esta regra se aplica a todos os atributos. Hei! Se eu inclui *, eu também posso incluir o nome de um atributo padrão em minhas classes, como, por exemplo, “nome”? Claro! Veja o exemplo abaixo:


grails.gorm.default.constraints = {
'nome'(nullable:false, maxSize:128)
}

Não é legal? Bom: esta é uma constraint global, mas o que seria uma constraint compartilhada?

Uma constraint compartilhada é uma regra de validação que pode ser aplicada em mais de um local nas suas classes de domínio. Vamos supor que você tenha vários atributos os quais devam, obrigatóriamente, possuir no máximo 20 caracteres. Você incluiria no mesmo arquivo um código similar ao abaixo:


grails.gorm.default.constraints = {
compartilhada(maxSize:20)
}

E para usá-la em suas classes de domínio, a declararia tal como abaixo:


class Testolina {
String rua
static constraints = {
rua (shared: "compartilhada")
}
}

Fácil e direto ao ponto. Eu amo isto!

Como validar constraints

Ok, você sabe como declarar suas regras de validação. Agora precisa saber como usá-las. No caso do Grails, toda classe de domínio possui injetado um método chamado validate. Caso alguma regra de validação falhe, a variável interna errors da classe de domínio será alterada incluindo os atributos que estão com problema. O código abaixo ilustra bem a situação:


dominio.validate()
if (dominio.hasErrors()) {
//danou-se! Há erros aqui!
}

O que pouca gente sabe é que você pode validar apenas alguns campos ao invés de todos se quiser. Como fazer isto? Simples: passe para o método validate() uma lista contendo os nomes dos campos a serem validados, tal como no código abaixo:


dominio.validate(['nome', 'email'])
if (dominio.hasErrors()) {
//danou-se! ou o atributo nome ou o atributo email estão ferrados! Droga!
}

E este atributo errors hein?

Como mencionei, toda classe de domínio possui injetado um atributo chamado errors. Este na realidade é uma instância da interface Errors do Spring. E sabe o que é mais legal nesta interface? Ela tem um método chamado reject implementado de varias formas.

Se for necessário incluir uma validação que vá além das constraints, vamos supor, alguma regra de negócio maluca, você poderia incluir uma validação ALÉM das constraints. Vamos supor que por alguma razão sinistra da vida, você precise adicionar uma validação que não seja feita por constraints (por exemplo, algo a ser validado ANTES do método save ser executado). Você poderia escrever um código como o abaixo:


class DominioMaluco {
def  beforeQualquerCoisa= {
if (condicaoMaluca) {
errors.reject("codigo.do.erro")
}
}
}

No caso, o método reject está adicionando ao atributo errors um novo código de erro. Aquele mesmo tipo de código de erro que incluimos nas mensagens de internacionalização padrão do Grails.

Sugiro que você dê uma lida nesta interface. Ela vai te possibilitar ir BEM além da validação padrão oferecida pelo framework.

Concluindo

Neste post vimos algo além do feijão com arroz que normalmente encontramos sobre o assunto. Como ficou claro, as constraints possuem uma profundidade muito maior que a esperada.

Minha sugestão para quem estiver começando no assunto é a seguinte: tenha nos seus bookmarks o link que expus acima contendo as constraints default do Grails. Estando habituado com estas, estude seu código fonte (é surpreendentemente simples).

Tendo feito isto, parta para as constraints customizadas e, em seguida, caso alguma coisa muito sinistra aconteça, ai sim use e abuse da API de validação do Spring, que é a base em cima da qual as constraints são construídas.

Grails: entendendo a busca por critérios (criterias)

Um dos recursos mais mal compreendidos do Grails são as criterias (será que posso falar “critérias” no plural?). O objetivo deste post é ir além do feijão com arroz que encontramos na documentação oficial. Espero que assim algumas das dúvidas mais comuns que escuto a respeito deste recurso sejam resolvidas.

Vamos tratar de uma situação típica: uma árvore genealógica bem idiota, representada pelas três classes de domínio abaixo:

class Pai {

String nome

String sobrenome

static hasMany = [filhos:Filho]

String toString() {this.nome}

}

class Filho {

String nome

String sobrenome

Integer idade

static belongsTo = [pai:Pai]

static hasMany = [filhos:Neto]

String toString() {this.nome}

}

class Neto {

String nome

String sobrenome

String status

static belongsTo = [pai:Filho]

String toString() {this.nome}

}

Com base nesta estrutura de classes, criei uma hierarquia que representei no grafo abaixo, que servirá para que possamos trabalhar neste post.

Tá: mas o que é uma criteria?

Grails nos oferece três ferramentas para que possamos fazer consultas em um banco de dados: finders dinâmicos, HQL (Hibernate Query Language) e criterias. A busca por critérios do Grails é na realidade uma abstração do mesmo recurso oferecido pelo Hibernate. A diferença é que temos aqui uma versão mais “groovy”, uma DSL baseada nos builders do Groovy.

Por que usar criterias se já tenho os lindos finders dinâmicos?

Os finders dinâmicos são feitos para consultas simples que envolvam algo entre uma e três condições no máximo (minha opinião). Se for necessário buscar os registros levando como base o atributo de um atributo composto da sua classe, esqueça os finders dinâmicos. Exercício pra você: retorne usando finders dinâmicos todos os netos de “Joe Lonely” em nosso exemplo.

Por que usar criterias se já tenho HQL?

A HQL já nos permite criar consultas BEM mais avançadas que os finders dinâmicos e com mesmo nível de complexidade que as critérias. Além disto, tanto os finders dinâmicos quanto as critérias geram, no frigir dos ovos, HQL. Sendo assim, porque usar uma camada superior se posso ir direto ao HQL? Simples: porque a concatenação de strings uma hora ou outra aparece quando trabalhamos com consultas REALMENTE complexas usando HQL.

E acredite: concatenação de strings pode não gerar tragédias no primeiro dia, mas quando você menos esperar elas virão atrás de você (elas são terríveis!).

O Básico

Toda classe de domínio do Grails possui injetada a função createCriteria, que retorna um objeto do tipo grails.gorm.HibernateCriteriaBuilder. Esta classe, por sua vez, possui uma função que será usada em 99% dos casos: a função list.

Na prática, caso desejemos listar todos os netos do nosso exemplo, poderíamos escrever o código abaixo:


def criteria = Neto.createCriteria()
criteria.list {}

O método list sempre recebe como parâmetro uma estrutura hierarquica. Neste caso, como foi passada uma estrutura vazia, todos os resultados são retornados. Caso fosse de nosso interesse retornar todos os netos cujo nome comece com “Pentelho”, escreveriamos o código abaixo:


def criteria = Neto.createCriteria()
criteria.list {
like("nome", "Pentelho%")
}

Seriam retornados os netos “Pentelho Lonely Joe” e “Pentelhox Lonely JOe”. No interior desta estrutura incluimos as expressões que irão compor a nossa consulta.

Dica: sempre pense na estrutura passada como um documento XML. Assim fica BEM mais fácil compreender o que está sendo feito.

É possível também usar os operadores lógicos or, and e not. No exemplo abaixo listaríamos todos os netos cujo nome comece com “Lori” ou “Pentelho”.


def criteria = Neto.createCriteria()
criteria {
or {
like("nome", "Pentelho%")
like("nome", "Lori%")

}
}

Observe que neste exemplo não usei a função list. Se passarmos diretamente o builder para o objeto criteria, usando a invocação dinâmica de métodos do Groovy, este já detecta que estamos na realidade chamando list. Trata-se apenas de uma conveniencia provida pelo framework.

A listagem completa de operadores suportados na busca por critérios pode ser obtida neste link: http://grails.org/doc/latest/ref/Domain%20Classes/createCriteria.html

Operadores lógicos

As critérias brilham quando precisamos usar expressões que envolvam os operadores lógicos and e, principalmente, o or. A melhor maneira de expor a sua superioridade é expor uma situação na qual o uso destes operadores da merda: finders dinâmicos.

Tenha como exemplo a consulta abaixo:


Neto.findAllByNomeLikeOrNomeLikeAndStatus("Pentelho%", "Lori%", "Idiota")

Que consulta exatamente temos aqui?

  • (nome like “Pentelho%” ou nome like(“Lori%”) e (status = “Idiota”) ?
  • (nome like “Pentelho%”) ou (nome like(“Lori%”) e (status = “Idiota”) ?

Na realidade será a segunda opção. Porém há espaço para dúvida. E se este existe, o que vocẽ faz? Você foge dele e usa a busca por critérios ora! Veja como consulta similar pode ser escrita de maneira MUITO mais clara:


def criteria = Neto.createCriteria()
criteria {
 or {
 like("nome", "Pentelho%")
 like("nome", "Lori%")
 }
 eq("status", "Idiota")

}

O operador or recebe como parâmetro um novo builder, aonde é possível incluir quantas operações, comparadores ou builders sejam necessários. Como resultado, têm-se um código ordens de magnitude mais fácil de entender e não sujeito a erros de interpretação por parte do programador.

Nota importante: por default, dentro de uma busca por critérios temos o operador and. Sendo assim, a consulta abaixo:

def criteria = Neto.createCriteria()
criteria {
     like("nome", "Pentelho%")
     eq("status", "Idiota")
}

Equivale à consulta

def criteria = Neto.createCriteria()
criteria {
     and {
         like("nome", "Pentelho%")
         eq("status", "Idiota")
     }
}

Só pra expor o funcionamento por trás dos panos, quando usamos os blocos or ou and, estamos na realidade criando novas instâncias das classes Conjunction e Disjunction, derivadas de org.hibernate.criterion.Junction. Pra quem já trabalhou com Hibernate, fica nítido o fato de novamente aqui não haver NADA de novo (o que é ótimo!) :)

Criterias dinâmicas

Como mencionei acima, um dos objetivos da busca por critérios é evitar a concatenação de strings na construção de consultas. Um recurso muito pouco documentado das critérias é a possibilidade de construí-las em tempo de execução.

O código abaixo expõe um exemplo:


def valorNumerico = 1
def tipoBusca = "sobrenome"
def valorSobrenome = "Lonely"

def criteria = Pai.createCriteria()
criteria {
if (valorNumerico == 1) {
eq("nome", "Joe Lonely")
} else {
like("nome", "%o%")
}

if (tipoBusca)
eq("sobrenome", valorSobrenome)
}

Olha que legal: você pode incluir instruções condicionais como if, else e switch no interior do builder. Assim, este pode ser construído em tempo de execução. É um recurso poderosíssimo e, infelizmente, pouco exposto em exemplos. Fica como exercício implementar o mesmo código acima usando HQL. Produzir algo similar com finders dinâmicos é impossível (se você conseguir, poste nos comentários aqui ok?).

O atributo de um atributo

É muito comum situações nas quais é necessário buscar registros com base no atributo de um dos seus atributos compostos. Imagine que queiramos buscar todos os filhos de “Joelinho Lonely” cujo nome comece com “Pentelho”. Simples: basta adicionar, ao invés de uma operação de comparação, o nome do próprio atributo na criteria, tal como no código abaixo:


def criteria = Neto.createCriteria()
criteria {
pai {
eq("nome", "Joelinho Lonely")
}
like("nome", "Pentelho%")
}

Observe que interessante: estamos incluindo um builder dentro de outro. No caso, um dos nós do nosso builder é o atributo “pai” da classe “Neto”. Em seu interior, podemos incluir quantas expressões quisermos, inclusive outros builders. Pra complicar um pouco mais a situação, vamos buscar todos os netos cujo nome comece com “Pentelho”, sejam filhos de “Joelinho Lonely” e netos do bom e velho “Joe Lonely”.


def criteria = Neto.createCriteria()
criteria {
 pai {
 eq("nome", "Joelinho Lonely")
pai {
eq("nome", "Joe Lonely")
}
 }
 like("nome", "Pentelho%")
}

Buscando um pai pelos filhos

Ok, agora vamos supor que queiramos buscar todos os registros da classe “Filho” a partir dos filhos que possuem. Retornando à implementação da classe filho, vemos que há um atributo filhos nesta. Haveria como, por exemplo, buscar todos os registros de Filho que tenham gerado netos cujo nome comece com “Pentelho”? Yeap! Veja o código abaixo:

def criterio = Filho.createCriteria()

criterio {

filhos {

like("nome", "Pente%")

}

}

Concluindo

Espero que com estas duas características pouco conhecidas das critérias eu tenha ajudado a elucidar algumas dúvidas que costumam aparecer aos iniciantes em Grails. Para quem já trabalhou com Hibernate, fica nítido que estamos lidando exatamente com o mesmo recurso, porém com uma roupagem mais legível.

Para de fato entender a fundo este recurso, recomendo a leitura dos seguintes links:

Groovy Builders – compreender os builders do Groovy ajuda muito a entender outros recursos presentes no Grails como por exemplo as constraints (será meu próximo post, aguardem)

Hibernate Criteria – como funcionam as critérias do Hibernate, que é o pano de fundo deste recurso.

org.grails.HibernateCriteriaBuilder – O código fonte por trás do funcionamento da busca por critérios

Só pra lembrar, você deve usar as buscas por critérios se um dos casos abaixo aparecer:

  • A consulta envolve mais de duas ou três condições
  • É necessária a inclusão de operadores lógicos como or, and e not
  • Você deseja construir a consulta em tempo de execução e você quer evitar concatenação de strings
  • É necessário aumentar a legibilidade do código de suas consultas

Quanto à última razão, peço perdão ao leitor. Infelizmente, o renderizador de código deste blog é muito primitivo e não expõe corretamente a indentação (mas pretendo resolver isto em breve).

Bom, até a próxima então. Meu próximo assunto será ou um detalhamento maior sobre a validação em Grails ou algum novo experimento envolvendo desenvolvimento de jogos usando HTML5 (ou serão os dois de uma vez?)

Grande abraço!