Gorm gerando join table sem primary keys
27/03/2012 19:09
0
Olá galera,

eis o problema: eu tenho um objeto TemplateHistory que possui vários objetos do tipo Variable. Caso típico de criar uma tabela intermediária. Então é isso que o GORM faz, como esperado. Até aí tudo bem. Acontece que quando ele gera essa tabela de relacionamento (no caso template_history_variable), é gerada sem primary keys. O normal seria criar 2 primary keys (uma de cada tabela), correto? Então tá gerando assim:

template_history_id bigint
variable_id bigint bigint (FK)
variables_idx integer (essa é gerada automaticamente pelo gorm não sei porquê)

O meu código tá assim :

class TemplateHistory {

long templateId
String body
Date timestamp
App app
Tester tester
List<Variable> variables

static hasMany = [variables:Variable]


E a classe Variable:

class Variable {

String name

static belongsTo = [templateHistory:TemplateHistory]


Alguém tem alguma ideia de porque isso acontece? Alguma forma de driblar a geração errada por parte do GORM?

Obrigado!
Tags: gorm,hasMany,oneToMany


0
O GORM, por padrão, já injeta em todas as classes um id, uma data da criação e uma data de lastUpdate nas suas classes de domìnio.

Outra coisa... é que eu acredito que esse "List<Variable> variables" seja desnecessário, pois o hasMany já diz ao Grails que se trata de uma lista.

Faça uma classe mais simples, só com essas outras coisas e veja o banco como fica... e avisa a gente ;)

Abs
27/03/2012 19:40


0
Tudo bem Rafael, fiz as alterações. Acontece que se eu tirar somente "List<Variable> variables" da classe TemplateHistory, o GORM gera uma foreign key em Variable e nada mais. Nada de tabela. O manual do Hibernate inclusive fala que evita fazer isso, visto que o Hibernate tem problemas de consistência conhecidos nesses casos de One to Many. O Grails não dava suporte a foreign key em relacionamentos desse tipo pelo mesmo motivo. Enfim, é somente por isso que eu quero mesmo usar uma tabela.
Bom, mas aí se eu tirar o belongsTo de Variable eu volto a ter uma tabela de relacionamento entre as template_history e variable. Agora ele gera assim:

template_history_variables_id bigint (FK),
variable_id bigint (FK)

Novamente: Cadê as primary keys?? Tô usando Grails 2.0. Será que isso num é bug do GORM não? Vocês conseguem simular isso?
27/03/2012 21:09


-1
Tiago você vai ter que usar o mapping para que os ids fiquem da maneira desejada:
http://grails.org/doc/latest/guide/GORM.html#identity
28/03/2012 11:33


0
Olá, Tiago.

Amigo, você poderia postar aqui como ficaram as estruturas das tabelas no banco? Eu sinceramente ainda não entendi totalmente como ficaram suas tabelas.

E mais: muitas mudanças nas classes de dominio costumam gerar a necessidade do "clean" do Grails


0
Castiel, como posso usar static mapping na tabela intermediária se ela é gerada pelo GORM e não por alguma classe do meu domínio?

Minha estrutura no banco ficou assim:

TemplateHistory gera uma tabela normal com chave primária(id) e tudo mais (nada anormal aqui).

Variable tb gera uma tabela normal (nada de mais aqui tb)

Como TemplateHistory possui muitos Variable (hasMany), eu preciso de uma join table para indicar quais Variables pertencem a um TemplateHistory. Essa join table, que o GORM gera com um nome template_history_variables, só tem 2 campos, o que é correto:

template_history_variables_id bigint [FK - chave estrangeira vinda de template_history],
variable_id bigint [FK - chave estrangeira vinda de variables]


A minha dúvida é: por que essas chaves não são geradas como primárias tb? Além disso, elas não são criadas com constraints 'NOT NULL', o que é estranho.

É isso. Qualquer outra coisa, avisem por favor =)
28/03/2012 13:44


0
Estou com esse mesmo problema!
A minha jointable está sem PK.

Como configurar as 2 FKs geradas nessa tabela como PK Dupla?


0
Tiago,

Pelo o que entendi, você está utilizando essa abordagem (tabela de relacionamento), por problemas de consistência do Hibernate, correto?

Estou desenvolvendo um sistema em Grails e existem varios one-to-many implementados com hasMany, ou seja, onde a foreign-key fica na classe que tem o belongsTo. Até agora não tive problema de consistência.

Gostaria de entender melhor esse problema, pois ainda não está em produção e posso fazer alguns ajustes. Vc poderia me passar o link que fala sobre isso?
12/03/2013 11:49


0
Luiz,

Vou falar do meu problema. Talvez seja o mesmo do Tiago.

Quando o Grails gera a JoinTable por causa do HasMany(), ele não gera as PK's dessa tabela.
Se você abrir o banco pelo NAVICAT ou algum outro software, ele irá reclamar que não existe PK nessa tabela.

Estou utilizando Grails 2.2.1
Banco de Dados: Mysql 5.6
Gerenciador de Banco: Navicat Premiun 10.0.6

Gostaria de saber se existe algum meio de definir uma PK dupla na hora de gerar essa joinTable?

Código que estou utilizando:
static mapping = {
episodios joinTable: [column: "episodio_id", key: "container_id"], composite:['container_id','episodio_id']
}

O que estou fazendo de errado?
Obrigado


0
Olá Luiz Gustavo,

Qual é o seu domínio? Vc pode postar as duas classes?
12/03/2013 13:58


0
Tiago,

Sobre a sua dúvida:

* o uso do belongsTo na classe Variable e hasMany na classe TemplateHistory não faz com que uma terceira tabela seja gerada. Essa combinação, coloca uma chave estrangeira na tabela da classe do belongsTo (nesse caso, a tabela VARIABLE terá um campo que se chama TEMPLATE_HISTORY_ID).

* a chave _idx está sendo criada, pois você está fazendo uma declaração dupla List<Variables> variables e static hasMany = [variables:Variable].

Se você declarar da maneira abaixo, apenas uma chave estrangeira será gerada.


List<Variables> variable
static hasMany = Variable


Sobre criar uma terceira tabela para evitar problemas de consistência, eu não entendi. Qdo puder, me passa o link que fala sobre isso.



12/03/2013 15:08


0
Muito útil: http://www.saltwebsites.com/2012/grails-gorm-association-quick-reference

Criem um projetinho pra testar.

Habilitem o console do banco H2 no config, fazendo o seguinte:

Para acessar e ver como o banco está sendo criado, façam: http://localhost:8080/NomeProjeto/admin/dbconsole


environments {
development {
grails.dbconsole.enabled = true
grails.dbconsole.urlRoot = "/admin/dbconsole"
grails.logging.jul.usebridge = true
}
production {
grails.logging.jul.usebridge = false
// TODO: grails.serverURL = "http://www.changeme.com"
}
}
12/03/2013 15:10


0
Esqueci de mencionar, as linhas acima devem ser colocadas no Config.groovy
12/03/2013 15:15


0
Seguem os domínios e no final o Bootstrap com alguns dados.

class Episodio {

int nr
float tamanho
String qualidade
String extensao
int largura
int altura
int durMiliseg
String duracao
int bitRate
String formato
String codec
String resolucao

Anime anime
public static belongsTo = [anime:Anime]

static constraints = {
nr(blank:false, nullable:false, minSize:1)
tamanho(blank:false, nullable:false, minSize:1)
qualidade(blank:false, nullable:false, inList:["FullHD","HD","LD","SD"])
extensao(blank:false, nullable:false, inList:["AVI","MKV","MP4","RM","RMVB","WMV"])
largura(blank:false, nullable:false)
altura(blank:false, nullable:false)
durMiliseg(blank:false, nullable:false)
duracao(blank:false, nullable:false)
bitRate(blank:false, nullable:false)
formato(blank:false, nullable:false)
codec(blank:false, nullable:false)
resolucao(blank:false, nullable:false)
}
}

class Container {

int nr
Date dtGravado
String tipo

static hasMany = [episodios:Episodio]

static constraints = {
nr(blank:false, nullable:false)
dtGravado(blank:false, nullable:false)
tipo(inList:["CD","DVD","Bluray","HD Externo","HD Interno"])
}

static mapping = {
episodios joinTable: [column: "episodio_id", key: "container_id"], composite:['container_id','episodio_id']
}
}

class Anime {

String titulo
String autor
Date dtLancamento
Date dtEncerramento
String info
Genero genero
String tipo

static constraints = {
titulo(blank:false, nullable:false, maxLength:255)
autor(blank:false, nullable:false, maxLength:255)
dtLancamento(blank:false, nullable:false)
info(blank:false, nullable:false)
tipo(blank:false, nullable:false, inList:["Filme","Manga","OVA","TV Serie","Outro"])
}
}

class Genero {

String genero
String descricao

static constraints = {
genero(blank:false, nullable:false)
descricao(blank:false, nullable:false)
}

String toString(){
return this.genero
}
}

--BOOTSTRAP
class BootStrap {

def init = { servletContext ->
System.setProperty( "file.encoding", "UTF-8" );
println System.properties
def genero = new Genero()
def anime = new Anime()
def episodio = new Episodio()
def episodio2 = new Episodio()
def container = new Container()

if(Genero.findAll().isEmpty())
{
println "Inserindo Genero"
genero = new Genero(
genero: 'Magical Girl',
descricao: 'Mahô shôjo ou mahou shoujo, são caracterizados por garotas com poderes mágicos'
)
genero.save()
if(genero.hasErrors())
{
println genero.errors
}

genero = new Genero(
genero: 'Shoujo-ai',
descricao: 'Romance entre personagens femininos'
)
genero.save()
if(genero.hasErrors())
{
println genero.errors
}

genero = new Genero(
genero: 'Shounen',
descricao: 'São voltados para o público masculino jovem'
)
genero.save()
if(genero.hasErrors())
{
println genero.errors
}

genero = new Genero(
genero: 'Seinen',
descricao: 'Animes voltados para adultos e adolescentes masculinos'
)
genero.save()
if(genero.hasErrors())
{
println genero.errors
}

genero = new Genero(
genero: 'Josei',
descricao: 'Animes voltados para adultos e adolescentes femininos'
)
genero.save()
if(genero.hasErrors())
{
println genero.errors
}

genero = new Genero(
genero: 'Kodomo',
descricao: 'Em japonês significa criança, são voltados para crianças menores'
)
genero.save()
if(genero.hasErrors())
{
println genero.errors
}

genero = new Genero(
genero: 'Mecha',
descricao: 'Animes caracterizados por robôs gigantes'
)
genero.save()
if(genero.hasErrors())
{
println genero.errors
}

genero = new Genero(
genero: 'Ecchi',
descricao: 'Em japonês significa indecente. O nome origina-se da leitura da letra H em inglês. Contém humor sexual bem moderado'
)
genero.save()
if(genero.hasErrors())
{
println genero.errors
}

genero = new Genero(
genero: 'Hentai',
descricao: 'Em japonês significa anormal ou pervertido, usado para descrever animes pornográficos. No entanto, no Japão os termos usados são Poruno ou Ero'
)
genero.save()
if(genero.hasErrors())
{
println genero.errors
}

genero = new Genero(
genero: 'Kemono',
descricao: 'Animais'
)
genero.save()
if(genero.hasErrors())
{
println genero.errors
}
}

if(Anime.findAll().isEmpty())
{
println "Inserindo Anime"
anime = new Anime(
titulo: 'Anime 1',
autor: 'Luiz Gustavo',
dtLancamento: new Date(),
dtEncerramento: new Date(),
info: 'Info Anime 1',
genero: genero,
tipo: 'OVA'
)
anime.save()
if(anime.hasErrors())
{
println anime.errors
}

anime = new Anime(
titulo: 'Anime 2',
autor: 'Leonardo',
dtLancamento: new Date(),
dtEncerramento: new Date(),
info: 'Info Anime 2',
genero: genero,
tipo: 'OVA'
)
anime.save()
if(anime.hasErrors())
{
println anime.errors
}
}

if(Episodio.findAll().isEmpty())
{
println "Inserindo Episodio"
episodio = new Episodio(
nr: 1,
tamanho: 280.5,
qualidade: "HD",
extensao: "MKV",
largura: 1280,
altura: 720,
durMiliseg: 19800,
duracao: "00:24:21",
bitRate: 1987,
formato: "AVC",
codec: "MP10",
resolucao: "1280x720",
anime: anime
)
episodio.save()
if(episodio.hasErrors())
{
println episodio.errors
}

episodio2 = new Episodio(
nr: 2,
tamanho: 280.5,
qualidade: "SD",
extensao: "AVI",
largura: 640,
altura: 480,
durMiliseg: 19800,
duracao: "00:24:21",
bitRate: 1987,
formato: "AVC",
codec: "MP10",
resolucao: "640x480",
anime: anime
)
episodio2.save()
if(episodio2.hasErrors())
{
println episodio2.errors
}
}

if(Container.findAll().isEmpty())
{
println "Inserindo Container"
container = new Container(
nr: "1",
dtGravado: new Date(),
tipo: "DVD"
)
container.addToEpisodios(episodio).save()
container.addToEpisodios(episodio2).save()
container.save()
if(container.hasErrors())
{
println container.errors
}
}
}
def destroy = {
}
}


0
Luiz Gustavo,

Uma duvida: qual a relação entre Container e Episodio? É de 1 pra n?
12/03/2013 16:54


0
estou tentando entender o motivo de você precisar de uma joinTable?
12/03/2013 17:05


0
Luiz Gustavo,

Algumas observações:

O código abaixo está redundante:


Anime anime
public static belongsTo = [anime:Anime]


Você pode retirar o Anime anime, pois você já declarou uma variável do tipo Anime no belongsTo.

Será que não está faltando um hasMany dentro de Anime para Episodio?


static hasMany = [episodios:Episodio]


Se a relação entre Episodio e Container for de n para n então você precisa ou utilizar o many-to-many do Grails normal ou utilizar uma classe que representa esse relacionamento. Essa classe vai fazer exatamente o papel da declaração joinTable que você tem.
12/03/2013 17:20


0
Luiz,

Sim 1 container pode ter n episodios.
Na verdade eu poderia fazer de 2 maneiras:
[list=1]
Colocar uma PK (id_container) na tabela Episodio: dessa forma tenho que necessariamente ter um container para depois inserir os episodios (não é o que pretendo).

Usar o hasMany e criar uma join table: dessa forma eu posso cadastrar os Episódios primeiramente e depois dizer em qual midia eles foram gravados (é o que quero).
[/list]


0
Certo Luiz Gustavo. Entendi.

Seu caso é parecido com o relatado aqui: http://stackoverflow.com/questions/4674222/grails-one-to-many-mapping-with-jointable

Não li tudo completamente, mas parece que a solução foi exatamente criar a 3a Classe (de relacionamento). Tem até uma implementação de exemplo.
12/03/2013 17:31


0
Entendi,

Queria fugir de uma implementação manual, mas acho que não vai ter jeito mesmo.

Obrigado =)


0
Ok.

tenho várias n x n implementadas manualmente. Muitas vezes preciso de um campo a mais que a n x n do Grails não me dá. Aliás, sobre esse assunto, cito http://www.ibm.com/developerworks/java/library/j-grails04158/

Onde é dito:

"If you think that two objects share a simple many-to-many relationship, you haven't looked closely enough at the domain. There is a third object waiting to be discovered with attributes and a life cycle all its own.""
12/03/2013 17:44



Ainda não faz parte da comunidade???

Para se registrar, clique aqui.


Aprenda Groovy e Grails com a Formação itexto!

Newsletter Semana Groovy

Assinar

Envie seu link!


Livro de Grails


/dev/All

Os melhores blogs de TI (e em português) em um único lugar!

 
Creative Commons
RSS Grails Brasil é mantido por itexto Consultoria.
Em caso de problemas contacte Henrique Lobo Weissmann (Kico) por e-mail: kico@itexto.com.br
Todo o conteúdo presente neste site adota o Creative Commons como licença padrão.
Ver: 4.14.0
itexto