Grails 1.0.1 e many-to-many com GORM DSL

Oi pessoal,

primeiro queria divulgar o forum do GrailsBrasil ( www.grailsbrasil.com ). Um ótimo espaço para os brazucas, que cada vez mais descobrem e se encantam com o Grails.

Em seguida, queria falar, com certo atraso, da tão esperada Release 1.0 do Grails, que na verdade já está na 1.0.1. Isso é uma grande notícia.

Por último, a razão deste post....

O Grails 1.0.1 possui o GORM DSL, que oferece grande facilidade para controlar detalhes do mapeamento objeto relacional dos objetos de domínio. Ao migrar uma aplicação do grails 0.6 para o 1.0.1, quis alterar alguns detalhes do mapeamento, já que agoraeu conseguiria isso sem precisar apelar para mapeamento Hibernate com XML.

O problema que eu queria resolver era:
Digamos que eu tenha uma classe Usuario e outra Privilegio, com um relacionamento many-to-many entre elas. Logo teremos:


class Usuario {
String nome
static hasMany = [privilegios:Privilegio]
}

class Privilegio {
String nome
static hasMany = [usuarios:Usuario]
static belongsTo = Usuario
}

Com as classes acima, o grails gera as seguintes tabelas:
usuario
id
nome

privilegio
id
nome

usuario_priv
usuarios_id (que é uma FK para privilegio.id)
privilegios_id (que é uma FK para usuario.id)
É isso mesmo: o campo usuarios_id é uma chave estrangeira para o id da tabela privilegio; e o campo privilegios_id é uma chave estrangeira para o id da tabela usuario. A explicação pra isso está aqui (vejam o comentário do Graeme).

Eu não sei quanto a vocês, mas pra mim isso soa muito estranho. Mas é assim mesmo que o grails gera por padrão. A forma que eu gosto de trabalhar é com campos no singular e com o nome do campo da chave estangeira condizente com o nome da tabela de referência:
usuario_priv.usuario_id = usuario.id
usuario_priv.privilegio_id = privilegio.id

Para isso, então, eu antigamente teria que fazer o mapemanto com XML do hibernate (já fiz vários para outras aplicações com grails). Mas agora na versão atual posso fazer o que quero com o GORM DSL. E é isso que quero mostrar, pois há um detalhe que precisei fazer alguns testes até entender o comportamento do Grails com essa DSL. Vamos lá...

class Usuario {
String nome
static hasMany = [privilegios:Privilegio]

static mapping = {
privilegios column:'usuario_id', joinTable:'usuario_priv'
}
}


class Privilegio {
String priv
static hasMany = [usuarios:Usuario]
static belongsTo = Usuario

static mapping = {
usuarios column:'privilegio_id', joinTable:'usuario_priv'
}
}


O que eu acho curioso e anti-intuitivo no código acima, é que numa primeira tentativa eu coloquei:
class Usuario {
static mapping = {
privilegios column:'privilegio_id', joinTable:'usuario_priv'
}
}
Mas isso acabou sendo errado. Na verdade temos que colocar como nome da coluna (para ter o resultado que eu esperava), o nome da classe em que me encontro, e não o nome do atributo de relacionamento. Em fim: o correto é o código acima completo das classe Usuario e Privilegio, e não o código acima com fundo avermelhado.

Fiz questão de colocar isso aqui, pois a documentação não me parece muito clara quanto a isso. (talvez eu é que tenha entendido errado a documentação, sei lá....em fim....tá aí pra quem precisar deste detalhe).


Abcs
Felipe

4 comentários:

Lucas Fais 1 de março de 2008 00:22

class Privilegio {
String priv
static hasMany = [usuarios:Usuario]
static belongsTo = Usuario

static mapping = {
usuarios column:'privilegio_id', joinTable:'usuario_priv'
}
}

Não sei se foi isso que vc quis dizer, mas eu entendo que, na verdade, o que temos que colocar no mapping, é o nome da Collection utilizada no hasMany. Assim, caso exista outro hasMany, o GORM consegue saber "de qual relacionamento se trata". Nesse caso usuarios.

Parabéns pelo Blog!

Felipe Nascimento 3 de março de 2008 11:07

Olá Lucas,

na verdade, não foi esse o foco. O foco é no mundo relacional, e não nos atributos dos objetos. O que quis mostrar é que na classe Privilegio, por exemplo, o mapping de usuarios (como vc colocou em bold), tem como valor do column o campo privilegio_id, e nao usuario_id, como eu acharia mais natural de pensar. Só isso. Acho que ao fazer o mapping rapidamente, algumas pessoas poderiam fazer "usuarios column:'usuario_id'" , ao invés da forma correta que é "usuarios column:'privilegio_id'". Abraços e obrigado pelo comentário.

Cris 13 de abril de 2008 14:24

Parabens pelo blog, já me ajudou mt.. tb achei pouco intuitivo a maneira como o gorm mapeia na base de dados.. mas de qq forma funciona da maneira que seria esperada pelos testes que fiz, mas o meu problema surge quando tento criar as views para um relacionamento deste tipo.. sera que voce sabe de algum link pra um exemplo simples e funcional completo com views para este tipo de relacionamento.. (de 1-M tem um montao, e todo mundo diz pra procurar o M-M noutro sítio.. onde?! nenhum tem um exemplo completo..)

Abraços

Felipe Nascimento 14 de abril de 2008 10:14

Olá Cris

o Grails não gera as views quando o relacionamento é many-to-many. Um exemplo que gosto, e acho que soluciona bem este caso, é a solução que o Matt Raible usa numa nova aplicação gerada pelo AppFuse.
Dê uma olhada aqui:
http://demo.appfuse.org/appfuse-spring/login.jsp
Entre com login admin e senha admin.
Depois, no menu Administração, acesse Visualizar Usuários. Em seguida clique no botão Adicionar para ir para o formulário de criação de novo usuário. Neste formulário, há uma solução de view para o relacionamento many-to-many entre Usuário e Perfil, lá em baixo (veja Atribuir Perfis).
Ele usa dois listboxes do tipo multiple, e assim seleciona quais perfis o usuário tem.
Olhar o código fonte do AppFuse é uma boa inspiração, para Java (mas facilmente adaptável para Groovy).
Abcs