[hibernate] many-to-many

24 respostas
maresp

Tenho 3 tabelas para compor um relacionamento many-to-many: filial, material e filial_material.

A classe Filial:

public class Filial {

   ... //propriedades

   private Set materiais;

   /**
      * @return Returns the materiais.
      * @hibernate.set 
      * lazy = "true"
      * table = "filial_material"
      * @hibernate.collection-many-to-many 
      * class = "br.com.atmtec.bean.Material"
      * column = "mat_id"
      * @hibernate.collection-key
      * column = "fil_id" 
      */
   public Set getMateriais() {
      return materiais;
   }
   ...// métodos
}

O que não ficou claro pra mim é: Devo criar uma classe FilialMaterial que representa a tabela ‘filial_material’ e contenha relacionamentos many-to-one para as outras duas tabelas? Seria algo assim:

public class FilialMaterial {
   
   private Filial filial;
   private Material material;
    
   /** 
    * @hibernate.many-to-one
    * column = "fil_id"
    */
   public Filial getFilial() {
      return filial;
   }

   /**
    * @hibernate.many-to-one
    * column = "mat_id"
    */
   public Material getMaterial() {
      return material;
   }
    
   ...// métodos
}

:?:

24 Respostas

_fs

Não, só precisa dos mapeamentos das pontas:

<bag lazy="true" name="listMateriais" inverse="true" cascade="none"
			table="Filial_Material">
			<key column="IDfilial" />
			<many-to-many class="Material"
				column="IDmaterial" />
		</bag>

edit: a propriedade “table” deve ser a tabela de ligação

maresp

Mas como faço pra manipular a tabela filial_material?

_fs

Hum, eu faço dois relacionamentos.

Por exemplo em filial:

<one-to-many class="FilialMaterial>
<many-to-many class="Material" table="Filial_Material">

Mas talvez haja outro jeito.

TedLoprao

Pq vc quer manipular a tabela filial_material?

Se ela só serve para o relacionamento, vc não precisa nem saber que a mesma existe… Ah não ser q vc tenha alguma outra informação na mesma, é esse o caso?

Fallow

eduardo_lopes

Ele precisa usar a tabela FIlial_Material para, por exemplo, listar os materiais por filial.

Eu também tenho um problema parecido, tabelas:

Cliente, Sistema, Cliente_Sistema.

Um Cliente pode ter vários Sistemas como um Sistema pode ter vários Clientes… também não consegui entender como mapear isso no hibernate. :?

Eduardo.

TedLoprao

Pois é, vc só precisa da tabela no banco… é isso que eu quero dizer… Vc mapeia como o Lipe colocou no post anterior:
Classe da Filial

<bag lazy="true" name="listMateriais" table="Filial_Material"> 
         <key column="IDfilial" /> 
         <many-to-many class="Material" column="IDmaterial" /> 
      </bag>

Classe do Material

<bag lazy="true" name="listFiliais" table="Filial_Material"> 
         <key column="IDmaterial" /> 
         <many-to-many class="Filial" 
            column="IDFilial" /> 
      </bag>

Pronto, é basicamente isso (salvo algum errinho, hehehe). Agora, vc tbm pode configurar o cascade se para um excluir do outro se for necessário…

Mas veja q vc não precisa manipular a tabela Filial_Material nesse caso…

Fallow

_fs

TedAloprao, talvez a tabela intermediária contenha main informações além das duas chaves primárias, como foi no meu caso.

Eduardo, faz dois many-to-many, um de cada lado horas:

Cliente:
- IDcliente
- IDsistema

Sistema:
- IDsistema
- IDcliente

Cliente_Sistema:
- IDcliente
- IDsistema

Cliente.java:
private List clientes;
get…
set…

Cliente.hbm.xml

<bag lazy="true" name="sistemas"
			table="Cliente_Sistema">
			<key column="IDcliente" />
			<many-to-many class="com.suaempresa.Sistema
				column="IDsistema" />
		</bag>

Sistema.java:
private List clientes;
get…
set…

Sistema.hbm.xml

<bag lazy="true" name="clientes"
			table="Cliente_Sistema">
			<key column="IDsistema" />
			<many-to-many class="com.suaempresa.Cliente
				column="IDcliente" />
		</bag>

Não resolve seu problema?[/code]

maresp

Tá, me explica como eu crio um relacionamento entre Filial e Material (ou trocando em miúdos, qual a operação que eu vou executar que ocasionará a criação de um registro na tabela ‘filial_material’?

_fs

Posso estar enganado (acabei de fechar tudo aqui), mas acho que basta:

Cliente cliente = ( Cliente ) session.load( Cliente.class, new Integer( 1 ) );

Filial filial = new Filial();
filial.setNome( "Filial 1" );

cliente.getFiliais().add( filial );
session.update( cliente );

Mas estou meio confuso, talvez seja:

Cliente cliente = ( Cliente ) session.load( Cliente.class, new Integer( 1 ) );

Filial filial = new Filial();
filial.setNome( "Filial 1" );
filial.setCliente( cliente );

session.save( filial );

Não lembro bem.

eduardo_lopes

Blza LIPE, vou tentar isso.

maresp

Certo LIPE, Ted, mas e quando eu tenho uma foreign key na tabela ‘filial_material’ ? Neste caso a minha fk é uma constraint, portanto quando ele tenta inserir o relacionamento o driver do banco lança uma exceção. Tem jeito de resolver isso sem mapear a tabela ‘filial_material’?

TedLoprao

Tentou habilitar o inverse=“true”?

Eu uso aqui e funciona tranquilo!

maresp

Ok Ted, funcionou. Mas neste caso ele desabilita insert/update nesta ponta (supondo que a outra ponta faz isso). Mas de onde virá o valor para a fk da tabela ‘filial_material’ no caso da inserção ser executada pela outra ponta?

TedLoprao

E aí Maresp, seguinte, eu usei essa configuração aqui e funcionou (Aí Lipe, vou usar teu exemplo pq tinha ele a mão para testar, huahuah)

<hibernate-mapping>
  <class name="hibernate.Candidato" table="candidato">
    <id name="idCandidato" column="IDCandidato" type="java.lang.Integer">
      <generator class="increment"/>
    </id>
    <bag name="formacoes" inverse="true" table="candidato_formacao" cascade="all" >
      <key column="IDCandidato" />
      <many-to-many class="hibernate.Formacao" column="IDFormacao"/>
    </bag>
  </class>
</hibernate-mapping>
<hibernate-mapping>
  <class name="hibernate.Formacao" table="formacao">
    <id column="IDFormacao" name="id" type="java.lang.Integer">
      <generator class="increment"/>
    </id>
    <property name="formacao" column="formacao" />
    <bag name="candidatos" table="candidato_formacao" cascade="all">
      <key column="IDFormacao" />
      <many-to-many class="hibernate.Candidato" column="IDCandidato"/>
    </bag>
  </class>
</hibernate-mapping>

Dai eu consegui criar um candidato e vincular uma formacao ao mesmo e salvar…
E tbm consegui criar uma Formacao e vincular um novo cliente e salvar…

Fallow

maresp

Ted, já tinha feito desta forma, acho que minha dúvida tá relacionada a isso aqui:

“TedLoprao”:
Pq vc quer manipular a tabela filial_material?

Se ela só serve para o relacionamento, vc não precisa nem saber que a mesma existe… Ah não ser q vc tenha alguma outra informação na mesma, é esse o caso?

Fallow

TedLoprao

Ahh, então vc possui um dado extra nessa tabela?? Bom, ai, q eu saiba, só mapeando a mesma! Vc pode manter o vinculo many-to-many por praticidade, mas terá que inserir através da propriedade vinculada a tabela filial_material.

Hehe, foi mal… Mas eu juro q tentei, :lol:

Fallow

maresp

O cara, valeu a força… vc respondeu sim… é que não tinha exposta a dúvida corretamente… valeu mais uma vez!

maresp

:arrow: http://hibernate.org/118.html#A11

_fs

Interessante o link, mas não vi nenhuma diferença considerável para refazer meus mapeamentos.

maresp

Lipe, como vc mapeia uma relação n-n sendo que a tabela de relacionamento conterá além das fk’s outra fk que não faz parte do relacionamento?

_fs

Um mapeamento e uma classe pra cada umas das três tabelas envolvidas ( Foo > Relashionship > Bar ).

Com isso dá para fazer os relacionamentos normais, sem usar o lance chique do composite-element.

Se a tabela de relacionamento possui outras FKs, basta declarar estes relacionamentos normalmente no .hbm dela.

Claro que daqui pra frente vou fazer de acordo com a recomendação deles, mas não vejo necessidade de alterar o que já está feito.

Arcadex

“TedLoprao”:
Ahh, então vc possui um dado extra nessa tabela?? Bom, ai, q eu saiba, só mapeando a mesma! Vc pode manter o vinculo many-to-many por praticidade, mas terá que inserir através da propriedade vinculada a tabela filial_material.

Hehe, foi mal… Mas eu juro q tentei, :lol:

Fallow

Por favor, estou com esse problema e não consegui chegar a uma conclusão…

Eu tenho tenho mais uma informação na tabela que faz o m:n (Cliente_Sistema).

Para o exemplo do Lipe:

Cliente:

  • IDcliente
  • IDsistema

Sistema:

  • IDsistema
  • IDcliente

Cliente_Sistema:

  • IDcliente
  • IDsistema
    - QTsistema (qtdade de qq coisa por exemplo)

Eu não posso, nessa nova situação, usar de alguma maneira a solução anteriormente postada pelo Lipe com alguma alteração??? (Talvez colocando o relacionamento m:n como chave composta. => IDcliente, IDsistema e QTsistema)

Por favor, se alguem souber uma maneira de dar carga em “Cliente_Sistema” sem precisar fazer um Cliente_Sistema.hbm.xml agradeço.

_fs

Sem fazer não tem como.

Arcadex

Lipe, o hibernate não trata essa situação? Colocar mais atributos na tabela cliente_sistema (alem das chaves estrangeiras)?

Quando existe apenas as chaves você faz:
(usando seu exemplo postado anteriormente)

Cliente cliente = ( Cliente ) session.load( Cliente.class, new Integer( 1 ) ); 

Sistema sistema = new Sistema(); 
sistema.setNome( "Nome do Sistema" ); 

cliente.getSistemas().add( sistema ); 
session.update( cliente );
Não poderíamos ter:
sistema.setNome("Nome do Sistema);
sistema.setQuantidade(25);
e depois com o
cliente.getFiliais().add( filial ); 
session.update( cliente );
Assim estaríamos carregando a tabela Sistema e Cliente-Sistema...

Viajei demais? :roll:

Criado 3 de setembro de 2004
Ultima resposta 5 de nov. de 2004
Respostas 24
Participantes 5