Lauro Becker

Java, Grails and other great things

Dao Artefacts Plugin

Introduction

That working with database using Grails is very trivial, everybody knows. You just have to write some domain mappings here, change some connections configurations there, and all works like a charm.

But, well, sometimes we have to do some additional coding around the conventions and we do need write some SQL instructions for a certain kind of database. This kind of thing commonly happens when you’re working with legacy systems. The best approach, with no doubt, would be to map everything as domain classes, and take all Grails’ advantages. But it’s not always possible.

The Problem

In my case, I’m in a project where we had to integrate an old hard coded Struts 1 app with Grails. I’m very lucky of getting everything working really fast with Struts 1 Plugin. Originally the application was developed over PostgreSQL, and now we’re running with Oracle too.  You should be thinking about DAO and DAO Factory patterns. And, I guess, it is the best solution for multiple datasource types.

Probably, before ORM’s era, you should have written tons of SQL instructions in hundreds of DAOs. Then, you always tried your very best for using SQL ANSI. So you’d never need to write specific SQLs. In the most of the situations you achieved it with no problems. But you always had to write some specific code for pagination purposes… Damn!

See how to do it with Oracle and PostgreSQL:

If you’re accustomed with Grails as I am, you may be thinking about how many lines you’ll have to write for achieving this. And, yes, it is a lot of code.

The Solution

I’m coming in this post to present you the new Dao Artefacts Plugin I’ve developed, for making my life easier – hope yours too. In fact, I hope you never need to write any DAO in your shinny Grails apps, but life is not always fair. ;)

Let’s say you have the above situation with Customer’s stuff – and have already installed the plugin. Create a new Groovy class inside grail-app/daos named CustomerDao. Put all your SQL ANSI inside it. Now create a subclass CustomerOracleDao and override all specific methods. Do the same for CustomerPostgresDao.

In your project’s Config.groovy, add the following lines:

grails.plugins.daoartefacts.datasource.name = "Oracle"
grails.plugins.daoartefacts.datasource.all = ["Oracle", "Postgres"]

The first is the actual database name. In this case, we are running over Oracle. The second line has a list of all known databases (for now, Oracle and Postgres).

When the application starts, the plugin looks for all classes inside grails-app/daos with *Dao.groovy pattern, and register them as Spring beans. If no per database class is created, the “ANSI” will be used. So, you don’t have to write factories. The magic happens by the Convention over Configuration paradigm.

Now you only need to inject your new DAO in your services. For example:

class CustomerService {
	def customerDao // Oracle or Postgres DAO, depending on the configuration

	void foo() {
		customerDao.foo()
	}
}

Conclusion

I’ve been seeing people putting native SQLs inside Grails Services. The main goal of this great framework is its productivity, but it doesn’t mean that design patterns should be left behind.

If you were missing a solution for Data Access tier, perhaps this plugin can fit.

Grails: Command Objects

Command Objects são objetos com funcionalidades parecidas às das classes de domínio, mas não servem para persistência de dados. Eles possuem capacidade de databinding e validação, comportamentos eventualmente requeridos para objetos que não representam nossas entidades. Quem já trabalhou com Struts 1 pode fazer uma analogia aos Forms.

Esta semana estive desenvolvendo um cadastro que necessitava de uma boa gama de validações. Essas validações variavam de acordo com a ação. Por exemplo, ao inserir era necessário executar as validações A, B e C. Já ao alterar, B, C e D deveriam ser processadas.

Para ilustrar o problema, vou utilizar um exemplo simplificado de manutenção de usuários. Imagine que temos a classe de domínio User:

class User {
	String username
	String password

	static constraints = {
		username blank: false, unique: true
		password blank: false, minSize : 6
	}
}

Ao criar um usuário, simplesmente nos preocupamos em garantir que username e password estejam populados e, neste caso, o password tenha no mínimo 6 dígitos. Obviamente o username deve ser único.

Depois de cadastrado, queremos permitir que o usuário altere sua senha. Para garantir um pouco de segurança, vamos solicitar que ele informe a senha atual, a nova senha e confirme a nova senha. O que inicialmente vem em mente é criar atributos transients na nossa classe User e relacionar as validações necessárias dentro de constraints. Eu não consigo visualizar como fazer isso de forma simples, sem uma série de testes confusos. A forma mais clara que encontrei foi utilizando Command Objects.

Crie uma classe chamada UserCommand, adicione as propriedades e defina as constraints, assim como você faria com uma classe de domínio. Pode ser em um arquivo separado, mas normalmente eles são declarados dentro do mesmo arquivo que o Controller. P.ex.:

class UserController {
	def update = { UserCommand cmd ->
		if (cmd.hasErrors()) {
			// ...
		}
	}
}

class UserCommand {
	String username;
	String actualPassword;
	String newPassword;
	String confirmNewPassword;

	static constraints = {
		// TODO As validações necessárias
	}
}

Submeta o formulário para a action update e observe:

  1. Os dados serão populados, e as conversões necessárias serão efetuadas;
  2. Automaticamente o método validate() será chamado.

Pronto! Não há muito mais o que explicar. Como normalmente acontece, com Grails temos códigos simples e eficientes.

Referência: http://grails.org/doc/1.0.x/guide/single.html#6.1.9 Command Objects

Referência: http://grails.org/doc/latest/guide/6.%20The%20Web%20Layer.html#6.1.10%20Command%20Objects

Grails: Personalizando nomes de chaves estrangeiras

É comum trabalharmos em projetos onde não temos total controle sobre a base de dados, e nossos sistemas devem se adaptar ao modelo. Também não é incomum encontrarmos chaves primárias compostas para as tabelas – vantagens e desvantagens dessa abordagem estão fora do escopo deste post.

Onde há chaves primárias compostas, presume-se que teremos chaves estrangeiras compostas. E, se você está trabalhando com Grails, isso não é tão simples de mapear quanto deveria ser. Não até a versão 1.3.7, ao menos.

Passando pelos problemas relatados em http://jira.grails.org/browse/GRAILS-4504, resolvi criar um workaround, baseado na solução proposta pelo Burt Beckwith, enquanto a resolução definitiva não chega.

Adicione ao seu projeto o arquivo disponível em http://organico.svn.sourceforge.net/viewvc/organico/trunk/src/groovy/br/com/organicadigital/organico/hibernate/OrganicoAnnotationConfiguration.groovy?revision=92 e altere o DataSource.groovy:

import br.com.organicadigital.organico.hibernate.OrganicoAnnotationConfiguration;

dataSource {
    pooled = true
    driverClassName = "…"
    configClass = OrganicoAnnotationConfiguration
}

Para o exemplo descrito no JIRA do Grails, seria feito algo assim:

class DeliveryInstruction {
    String description
    IntervalType intervalType
    IntervalQuantity intervalQuantity

    static constraints = {
        description(nullable:false, blank:false, size:3..250)
        intervalType()
        intervalQuantity()
    }

    static mapping = {
        table   'mail_delivery_instruction'
        version false
        id      generator:'sequence', params:[sequence:'DELIVERY_INSTR_SEQ']
        columns {
            id               column:'code'
            intervalType     column:'interval_type_code'
            //intervalQuantity column:'interval_quantity'
            description      column:'description'
        }
    }

    String toString() {
        return description
    }

    static foreigners = [
        intervalQuantity : [intervalType : "interval_type",
                          quantity: "interval_quantity" ]
    ]
}

O atributo foreigners é um mapa. Cada item deste mapa possui uma chave String (intervalQuantity) e os valores são outro mapa ([interval_type : “interval_type”, quantity: “interval_quantity” ]).

A chave intervalQuantity é o nome da propriedade que define o relacionamento. intervalType é o nome da FK sugerida pelo Grails, e interval_type o novo nome. Ok, ok, nesse exemplo não foi nada alterado… mas observe o atributo quantity.

É algo experimental, mas já foi testado em produção. ;)

Grails: DataSource em arquivo properties

Você desenvolveu um produto em Grails para somente um cliente e configurou a conexão com o banco de dados no arquivo DataSource.groovy, de acordo com os ambientes de desenvolvimento, teste e produção. Implantou o sistema e tudo funciona perfeitamente.

Certo dia seu cliente liga dizendo que está melhorando a infra-estrutura da empresa e decide trocar o banco de dados para outro servidor. Você, naturalmente, altera o arquivo DataSource.groovy, empacota a aplicação e atualiza no cliente.

Na outra semana esse servidor novo queima, e é necessário restaurar o backup (que certamente seu cliente faz periodicamente) para outro servidor. Lá vai você de novo. Abre sua IDE de desenvolvimento, altera a configuração para o novo IP, empacota a aplicação e atualiza.

Como seu produto é de qualidade, você arranja outro cliente. Ele, obviamente, possui suas próprias configurações de banco de dados (talvez até outro SGBD, o que não é problema utilizando GORM). E agora, como lidar com essas duas configurações? Criar um branch feio no seu sistema de controle de versão e, cada vez que o cliente alterar a senha do banco de dados, p. ex., você terá que empacotar e atualizar novamente? Seria, no mínimo, tedioso.

Então você tem a brilhante e original idéia de deixar as configurações de DB em um arquivo externo! Somente altera as configurações e, com um restart da aplicação e um pouco de sorte, ela sairá rodando normalmente.

“Legal! Como eu faço isso? Se fosse com JDBC eu simplesmente carregaria um arquivo properties de algum lugar e utilizaria essas informações para abrir a conexão. Mas é Grails e eu não faço a mínima idéia de como começar.”

Confesso que perdi um bom tempo até encontrar uma forma de fazer isso. E a solução é simples, com alguns pequenos detalhes que devem ser levados em consideração.

Crie um arquivo chamado datasources.properties em grails-app/conf e coloque o seguinte conteúdo:

dataSource.username=usuario
dataSource.password=senha
dataSource.url=url de conexão

Adicione a seguinte linha em Config.groovy:

grails.config.locations = [ "classpath:datasources.properties" ]

Abra o arquivo DataSource.groovy e deixe similar a esse:

dataSource {
    pooled = true
    driverClassName = "driver do seu banco de dados"
}
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 = "override in datasources.properties"
	          username = "override in datasources.properties"
	          password = "override in datasources.properties"
		}
	}
	test {
		dataSource {
		  url = "override in datasources.properties"
	          username = "override in datasources.properties"
	          password = "override in datasources.properties"
		}
	}
	production {
		dataSource {
		  url = "override in datasources.properties"
	          username = "override in datasources.properties"
	          password = "override in datasources.properties"
		}
	}
}

“Ãh?? O que é esse ‘override in datasources.properties’??” Já vai ficar claro. Não perca o foco :P. Continue lendo…

Execute um grails clean e suba a aplicação. Provavelmente você verá um erro dizendo que não foi possível encontrar a URL “override in datasources.properties”. Suba a aplicação de novo e, desta vez, tenha um pouco mais de fé.

TA-DAAA!

Mas o que aconteceu aí? Uma breve explicação: No ambiente de desenvolvimento, rodando com o script run-app, na primeira vez que você sobe por algum motivo o Grails não reconhece a configuração externa. Creio que seja um bug e deverá ser resolvido nas próximas versões. Na segunda vez ele reconhece o arquivo de properties e carrega de lá as informações.

Quando você for rodar em produção, fazendo deploy no Tomcat, p. ex., o arquivo properties será reconhecido de cara.

Conclusão: Não é a solução mais elegante. Aliás, falta muito para isso. Mas você precisava urgentemente disso para não continuar naquela rotina entediante de atualizações, lembra? :D

Uma solução muito melhor seria utilizando JNDI. Se quiser saber mais, dá uma conferida no blog do Lucas Teixeira.

Mais sobre configurações externas: http://grails.org/doc/latest/guide/3.%20Configuration.html#3.4 Externalized Configuration

Saudações!

Trabalho com Java EE desde 2005. Até 2009 utilizei vários frameworks, principalmente com Struts (I e II). Em 2009, após alguma certa insatisfação com a plataforma no que diz respeito a produtividade (interfaces de usuário e controladores, principalmente), comecei a estudar Ruby on Rails.

Fiquei boquiaberto com o poder do framework. No entanto me sentia deslocado pensando em todas as vantagens que Java me proporcionava, e que não poderia mais usufruir. Pesquisei um pouco mais e achei o JRuby. Uau! Ruby (inclusive Rails) rodando em cima da JVM. Isso me pareceu fantástico (e realmente é). Podia então utilizar todo o legado Java que eu já conhecia e aproveitar as vantagens das duas tecnologias. Ao final de um mês olhei meu código e era, praticamente, Java. Alguma coisa ainda não estava no lugar.

Pesquisando por “java+rails” e variações, comecei a me deparar com um tal de Grails. Acessei o site oficial, vi aquele cálice e concluí: The search is over (perdoem-me o trocadilho). O ponto é que, para mim, realmente o Grails atendeu às necessidades de desenvolvimento de forma completa. Encontrei o que eu precisava.

O propósito deste blog é compartilhar um pouco das minhas experiências, com foco em Java e Grails. Se você não conhece esse fascinante framework, confira na Wikipedia uma breve descrição e, claro, leia os próximos posts. ;)

[]’s

Follow

Get every new post delivered to your Inbox.