Informações

Tipo: Tutorial
Data de Publicação: 03/11/2005
Revisado em: 03/11/2005

Vote!

  • Currently 3,8/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags Relacionadas

hibernate framework persistencia

Comentários ( 9 )

Imprimir

Mapeando Associações com Hibernate - Parte 1

por:

Raphaela Galhardo Fernandes (raphaela@jeebrasil.com.br)
Gleydson Lima (gleydson@jeebrasil.com.br)

Este tutorial apresenta os mapeamentos no Hibernate de relacionamentos entre entidades. Os mapeamentos apresentados são: 1-para-n, n-para-1 e n-para-n. O próximo tutorial consiste na segunda parte do tutorial sobre Associações, onde serão mapeadas associações n-para-n com atributos e 1-para-1.

Associações

O termo associação é utilizado para se referir aos relacionamentos entre as entidades. Os relacionamentos n-para-n, n-para-1 e 1-para-n são os mais comuns entre as entidades de um banco de dados.

Todos os exemplos apresentados nesta seção baseiam-se no diagrama de classes mostrado na Figura 1 do Tutorial "Primeiro Exemplo com Hibernate".

Associações 1-n (one-to-many)

Para exemplificar o relacionamento 1-n, considere o relacionamento entre a entidade Centro e a entidade Universidade da Figura 1. O relacionamento diz que uma universidade possui um conjunto de n centros e um centro está associado a apenas uma única universidade. Considere as classes de domínio Java de uma universidade e de um centro, respectivamente, mostradas nas Listagens 1 e 2.

JSP

Figura 1 - Relacionamento entre Centro e Universidade

package br.com.jeebrasil.dominio;
import java.util.Collection;
public class Universidade implements Serializable{
	private int id;
	private String nome;
        private Endereco endereco;
	private Collection centros;
	
	//Implementação dos métodos setter e getter
	...	
}

Listagem 1 - Classe de Domínio: Universidade

package br.com.jeebrasil.dominio;
import java.util.Collection;
public class Centro implements Serializable{
	private int id;
	private String nome;
      private Universidade universidade;
      //departamentos -> Atributo mapeado das mesma forma que a
      //coleção centros em Universidade
	private Collection departamentos;
	
	//Implementação dos métodos setter e getter
}

Listagem 2 - Classe de Domínio: Centro

A classe de domínio Universidade é a que possui um mapeamento do tipo 1-n. O seu mapeamento pode ser visto na Listagem 3. Neste momento, as informações do endereço da universidade foram desconsideradas.

...
<hibernate-mapping>
    <class name="br.com.jeebrasil.dominio.Universidade"   
           table="UNIVERSIDADE">
        <id name="id" column="ID_UNVERSIDADE" type="int">
            <generator class="increment"/>
        </id>         
        <property name="nome"/>     
   		
	  <!-- Mapeamento da Coleção de centros -->
	  <set name="centros" inverse="true" lazy="true">
		<key column="ID_UNIVERSIDADE"/>
		<one-to-many class="br.com.jeebrasil.dominio.Centro"/>
	  </set>	       	
    </class>

</hibernate-mapping>  

Listagem 3 - Mapeamento 1-n: tabela universidade

Observa-se que para realizar o mapeamento 1-n, ou seja, da coleção centros foi utilizada uma tag set. Um set ou um conjunto representa uma coleção de objetos não repetidos que podem ou não estar ordenados. O atributo name define a propriedade que está sendo tratada para realizar o relacionamento 1-n. O atributo key representa a coluna da tabela relacionada (Centro) que possui a chave estrangeira para a classe Universidade. O nome da coluna da chave estrangeira (ID_UNIVERSIDADE) é informado no atributo column. Na tag <one-to-many> informa-se a classe a qual pertence à coleção de objetos, no caso br.com.jeebrasil.dominio.Centro.

A tag set também apresenta um atributo denominado inverse. Esse atributo é utilizado para que o Hibernate saiba como tratar a associação entre duas tabelas. Quando um lado da associação define o atributo inverse como true, indica que a ligação do relacionamento entre a associação será de responsabilidade do "outro lado" da associação.

Considerando o relacionamento entre uma universidade e seus centros, no mapeamento do conjunto de centros em universidade, o atributo inverse é igual a true. Dessa forma, a criação ou atualização do relacionamento entre um centro e uma universidade será feita durante a persistência ou atualização de um objeto Centro.

A partir do exemplo apresentado na Listagem 4, cria-se uma instância da classe Universidade e define-se um valor para o atributo nome. Em seguida, uma instância da classe Centro também é criada, definindo o atributo nome e o atributo universidade (como sendo a instância de Universidade anteriormente criada). Por fim, o objeto Centro criado é adicionado à coleção de centros do objeto Universidade, o qual é persistido.

...
Universidade univ = new Universidade()
univ.setNome("Universidade Federal do Rio Grande do Norte");

Centro centro = new Centro();
centro.setNome("Centro de Tecnologia");
centro.setUniversidade(univ);

univ.setCentros(new HashSet());
univ.getCentros().add(centro);

session.save(univ);
...

Listagem 4 - Exemplificando uso do atributo inverse

A Listagem 5 apresenta o que acontece se o atributo inverse no mapeamento 1-n for definido como false. O que acontece é que o Hibernate insere uma linha na tabela UNIVERSIDADE e em seguida tenta atualizar o relacionamento entre uma universidade e um centro, no caso a partir de um UPDATE na tabela CENTRO, setando a chave estrangeira para a tabela UNIVERSIDADE.

Hibernate: insert into UNIVERSIDADE (nome, ID_ENDERECO, ID_UNIVERSIDADE) values (?, ?, ?)
Hibernate: update CENTRO set ID_UNIVERSIDADE=? where ID_CENTRO=?

Listagem 5 – SQL gerado pelo Hibernate com atributo inverse=false

Verifica-se, então, que com o atributo inverse sendo igual a false, o Hibernate tenta atualizar o relacionamento entre uma universidade e um centro logo após a inserção da universidade. Neste caso, como o relacionamento com a universidade está sendo feito com um objeto transiente de um Centro, o que vai ocorrer é que o Hibernate vai tentar atualizar a chave estrangeira para a tabela UNIVERSIDADE em uma linha da tabela CENTRO que não existe, acontecendo o erro exibido na Listagem 6.

ERRO:
Exception in thread "main" org.hibernate.TransientObjectException: object 
references an unsaved transient instance - save the transient instance 
before flushing: br.com.jeebrasil.dominio.Centro ...

Listagem 6 – Erro gerado após teste com o atributo inverse=false no relacionamento 1-n em Universidade

De acordo com o mapeamento da Listagem 3, o atributo inverse é definido como true. Então, para o código da Listagem 4 vai ser gerado o SQL exibido na Listagem 7. Neste caso, veja que apenas é dado um INSERT na tabela UNIVERSIDADE, ou seja, no momento da inserção de uma universidade o Hibernate não atualiza o relacionamento entre ela e seus centros, pois espera que ele seja feito no momento da inserção/atualização de um objeto Centro.

Hibernate: insert into UNIVERSIDADE (nome, ID_ENDERECO, ID_UNIVERSIDADE) values (?, ?, ?)

Listagem 7 - SQL gerado pelo Hibernate com atributo inverse=true

Resumindo, se o atributo inverse não for definido como true, o Hibernate não tem como saber qual dos dois lados foi atualizado, ou seja, vai sempre atualizar os dois lados de uma vez, uma atualização para cada classe da relação, o que seria desnecessário. Caso contrário, o Hibernate passa a saber de qual lado fazer a atualização e fazendo uma única vez.

Na tag set também está presente o atributo lazy. Ele é utilizado para resolver o seguinte problema: quando se realiza um select em um objeto Universidade implica em serem feitos n (número de centros da universidade) outros select’s para buscar os seus centros. Dessa forma, a resolução do problema é feita apenas definindo o atributo lazy como sendo true. A coleção de centros passa a ser lazy-loading, o que significa que somente será recuperada quando solicitada, ou seja, a coleção de centros de uma universidade só seria solicitada caso o programador a acesse através da chamada ao método getCentros().

Associações n-1 (many-to-one)

O relacionamento n-1 será apresentado a partir do relacionamento <many-to-one> existente entre a tabela Centro e a tabela Universidade. Neste caso, o relacionamento está presente no mapeamento da classe Centro, como mostrado na Listagem 8.

...
<hibernate-mapping>
    <class name="br.com.jeebrasil.dominio.Centro"   
           table="CENTRO">            
        <id name="id" column="ID_CENTRO" type="int">
            <generator class="increment"/>
        </id>         
        <property name="nome" type="java.lang.String"/>     
   		
	    <!-- Mapeamento da Universidade -->
	    <many-to-one name="universidade" 
        	   class="br.jeebrasil.dominio.Universidade" cascade="none"
               fech="join" update="true" insert="true" lazy="true"
               column="id_universidade"/>
       
	
        <set name="departamentos" lazy="true" inverse="true">
		<key column="ID_CENTRO"/>
		<one-to-many class="br.com.jeebrasil.dominio.Departamento"/>
	</set>
    </class>
</hibernate-mapping>  

Listagem 8 - Mapeamento n-1: tabela centro

Como mostrado no mapeamento da classe Centro, o relacionamento n-1 é mapeado a partir da tag <many-to-one>. Essa tag apresenta um conjunto de atributos, que podem assumir os valores apresentados na Listagem 9.

 <many-to-one name="propertyName" 
     class="ClassName" column="column_name"
     fetch="join|select" update="true|false" lazy="true|false"
     insert="true|false" cascade="all|none|save-update|delete"
 />

Listagem 9 – Mapeamento n-1: atributos

  • name: nome do atributo na classe Java;
  • column: coluna do banco de dados. É uma chave estrangeira;
  • class: nome da classe Java da entidade relacionada;
  • insert e update: indica se o atributo será incluído e alterado ou somente lido;
  • cascade: indica com que ação em cascata o relacionamento será tratado.
    • none: associação é ignorada;
    • save-update:os objetos associados vão ser inseridos ou atualizados automaticamente quando o objeto "pai" for inserido ou atualizado;
    • delete: os objetos associados ao objeto "pai" vão ser deletados;
    • all: junção de delete e save-update;
    • all-delete-orphan: o mesmo que all, mas o Hibernate deleta qualquer objeto que tiver sido retirado da associação;
    • delete-orphan: se o objeto não fizer mais parte da associação, ele removido.
  • fetch: se definido como join é usado para realizar joins sem restrição de nulidade (outer-join). Se for select, um novo select é feito para recuperar a informação da associação.
  • lazy: se igual a true, o objeto só será recuperado se solicitado; se igual a false, o objeto sempre será recuperado.
Associações n-n (many-to-many)

O relacionamento n-n será feito a partir do relacionamento entre as entidades Departamento e Curso mostrado na Figura 2.

JSP

Figura 2 - Relacionamento n-n entre Curso e Departamento

Um relacionamento n-n implica em existir uma nova tabela para mapear o relacionamento no banco de dados. Vamos denominar essa nova tabela como DEPARTAMENTO_CURSO, como mostrado na Figura 3. Dessa forma, um departamento possui uma coleção de cursos e um curso uma coleção de departamentos. A existência dessas coleções é opcional. Por exemplo, pode ser que em um sistema real não seja necessário saber todos os departamentos de determinado curso, mas se for realmente necessário, o Hibernate apresenta outros mecanismos para a obtenção desta informação.

JSP

Figura 3 - Tabela de relacionamento DEPARTAMENTO_CURSO

As classes Java das entidades Departamento e Curso estão ilustradas nas Listagens 10 e 11, respectivamente.

package br.com.jeebrasil.dominio;
import java.util.Collection;
public class Departamento implements Serializable{
	private int id;
	private String nome;
	private String sigla;
	private Centro centro;
	private Collection professores;
	private Collection cursos;
	
	//	Implementação dos métodos setter e getter
}

Listagem 10 - Classe de Domínio: Departamento

package br.com.jeebrasil.dominio;
import java.util.Collection;
public class Curso implements Serializable{
	private int id;
	private int codigo;
	private String nome;
	private String sigla;
	private Collection departamentos;
	private Collection alunos;
	
	//Implementação dos métodos setter e getter
}

Listagem 11 - Classe de Domínio: Curso

A Listagem 12 apresenta o mapeamento da classe Departamento. Já a Listagem 13, o mapeamento da classe Curso.

...
<hibernate-mapping>
    <class name="br.com.jeebrasil.dominio.Departamento"  
           table="DEPARTAMENTO">            
        <id name="id" column="ID_DEPARTAMENTO" type="int">
            <generator class="increment"/>
        </id>          
        <property name="nome" type="java.lang.String"/>        
        <property name="sigla" type="java.lang.String
        <many-to-one name="centro"                            
                  class="br.com.jeebrasil.dominio.Centro"
		 	column="ID_CENTRO"
			cascade="save-update"/>		
	  <set name="professores">
	      <key column="ID_DEPARTAMENTO"/>
		<one-to-many class="br.com.jeebrasil.dominio.Professor"/>
	  </set>
		
	 <!-- Mapeamento dos cursos -->
	 <set name="cursos" table="DEPARTAMENTO_CURSO" 
            inverse="true">
		<key column="ID_DEPARTAMENTO"/>
		<many-to-many column="ID_CURSO" 
                   class="br.com.jeebrasil.dominio.Curso"/>
	 </set>		
   </class>
</hibernate-mapping>  

Listagem 12 - Departamento.hbm.xml

...
<hibernate-mapping>
    <class name="br.com.jeebrasil.dominio.Curso"  table="CURSO">        
        <id name="id" column="ID_CURSO" type="int">
            <generator class="increment"/>
        </id>          
        <property name="codigo"/>  
        <property name="nome"/>        
        <property name="sigla"/>      
	  <set name="alunos">
		<key column="ID_CURSO"/>
		<one-to-many class="br.com.jeebrasil.dominio.Aluno"/>
	</set>
		
	<!-- Mapeamento dos departamentos-->
	<set name="departamentos" table="DEPARTAMENTO_CURSO">
		<key column="ID_CURSO"/>
		<many-to-many column="ID_DEPARTAMENTO" 
                       class="br.com.jeebrasil.dominio.Departamento"/>
	</set>		
    </class>
</hibernate-mapping>    

Listagem 13 - Curso.hbm.xml

Observa-se que tanto no mapeamento da coleção cursos em Departamento quanto no da coleção departamentos em Curso, o relacionamento n-n é feito a partir de uma tag set. Os mapeamentos das duas coleções apresentam uma tag key, na qual o atributo column indica a chave estrangeira do pai na tabela de relacionamento DEPARTAMENTO_CURSO. Apresentam também a tag many-to-many utilizada para indicar a entidade filha e sua chave estrangeira no relacionamento. A única diferença entre o mapeamento das duas coleções é que na tag set de cursos no mapeamento da entidade Departamento o atributo inverse é igual a true, significando que na tabela DEPARTAMENTO_CURSO só será inserido o relacionamento entre as duas entidades, quando um curso for inserido no banco de dados associado a um departamento.

Neste caso, não seria necessário mapear a coleção de cursos em Departamento, pois não está sendo utilizada para popular a tabela de relacionamento. Como já citado, para recuperar a coleção de cursos de um departamento, o Hibernate apresenta outros mecanismos.

Em outros relacionamentos n-n, pode ser que seja necessário inserir/atualizar o relacionamento na tabela de relacionamento durante a inserção/atualização de qualquer um dos lados das entidades, portanto, basta que o atributo inverse nas duas coleções seja mapeado como false.

O código presente na Listagem 14 cria um instância de um objeto Departamento. Em seguida recupera um objeto persistente da classe Curso com identificador igual a 1, adiciona esse objeto Curso na coleção de cursos do objeto Departamento criado e, por fim, persiste o departamento. O SQL gerado é apresentado na Listagem 15. Observa-se que apenas um SELECT na tabela CURSO é feito e um INSERT na tabela DEPARTAMENTO. Não há uma inclusão de linha na tabela de relacionamento DEPARTAMENTO_CURSO, pois o atributo inverse da coleção de cursos no mapeamento da classe Departamento foi definido como true.

Departamento depart = new Departamento();
depart.setNome("Departamento 1");	    
Curso curso = (Curso)session.get(Curso.class, 1);
depart.getCursos().add(curso);
session.save(depart);

Listagem 14 - Persistência de Departamento

Hibernate: select curso0_.ID_CURSO as ID1_0_, curso0_.codigo as  
   codigo1_0_, curso0_.nome as nome1_0_, curso0_.sigla as sigla1_0_, 
   curso0_.ID_COORDENADOR as ID5_1_0_ from CURSO curso0_ where 
   curso0_.ID_CURSO=?
Hibernate: insert into DEPARTAMENTO (nome, sigla, ID_CENTRO, ID_CHEFE, 
   ID_DEPARTAMENTO) values (?, ?, ?, ?, ?)

Listagem 15 - SQL para comandos da Tabela 14

Já o código presente na Listagem 16, cria uma instância da classe Curso, busca o objeto persistente Departamento de identificador 1 na base de dados, adiciona o departamento a coleção de departamentos do objeto Curso e realiza a a sua persistência. O SQL presente na Listagem 17 mostra que um SELECT foi feito na tabela DEPARTAMENTO, um INSERT na tabela CURSO e um outro INSERT na tabela DEPARTAMENTO_CURSO. Nesse caso, o relacionamento foi persistido, pois no mapeamento da coleção departamentos da classe Curso, como o atributo inverse não foi definido como true, assume-se que ele é false.

Curso curso = new Curso();
Departamento d = (Departamento)session.get(Departamento.class, 1);
curso.getDepartamentos().add(d);
session.save(curso);

Listagem 16 - Persistência de Curso

Hibernate: select departamen0_.ID_DEPARTAMENTO as ID1_0_, 
   departamen0_.nome as nome4_0_, departamen0_.sigla as sigla4_0_, 
   departamen0_.ID_CENTRO as ID4_4_0_, departamen0_.ID_CHEFE 
   as ID5_4_0_ 
   from DEPARTAMENTO departamen0_ where departamen0_.ID_DEPARTAMENTO=?
Hibernate: insert into CURSO (codigo, nome, sigla, ID_COORDENADOR, 
   ID_CURSO) values (?, ?, ?, ?, ?)
Hibernate: insert into DEPARTAMENTO_CURSO (ID_CURSO, ID_DEPARTAMENTO) 
   values (?, ?)

Listagem 17 - SQL para comandos da Tabela 16

Conclusões

O mapeamento de relacionamentos entre entidades será freqüente ao se utilizar um framework de mapeamento objeto-relacional. De início, mapeá-los utilizando Hibernate pode parecer uma tarefa difícil e tediosa, porém nada melhor do que a prática para ver que esse poderoso framework ajudará bastante o desenvolvedor e possibilitará redução no tempo de desenvolvimento de uma aplicação. Pratique!

No próximo tutorial será apresentado o mapeamento do relacionamento 1-para-1 e do relacionamento n-para-n com atributos, onde também será apresentado o mapeamento de chaves compostas.

Referências

  1. Christian Bauer e Gavin King. Hibernate in Action. 2005.
  2. Grupo Hibernate. Hibernate Reference Documentation. Version 3.0.5. Obtido em http://www.hibernate.org
  3. Gleydson de Azevedo Ferreira Lima. Material Didático. 2005.
  4. Nick Heudecker. Introdução ao Hibernate.
  5. Maurício Linhares. Introdução ao Hibernate 3.
  6. Francesc Rosés Albiol. Introducción a Hibernate. 2003.
  7. Fabiano Kraemer, Jerônimo Jardel Vogt. Hibernate, um Robusto Framework de Persistência Objeto-Relacional. 2005.

Comentários (9)

Ótimo
postado por Aristides Vicente em 29/03/2007 às 23:21
Muito bom mesmo, de verdade. Parabéns!!!
postado por José Milton em 30/03/2007 às 23:21
Sinceramente... sem comentários. Muito bom esses tutoriais sobre hibernate. Agora que estou no terceiro, mas estou ancioso para ler todos. Muito bom mesmo. Parabéns!!!
postado por Marcio Correia em 10/04/2007 às 23:21
Parabéns por este e pelos outros artigos sobre Hibernate. Eu vi vários artigos na web sobre Hibernate. Os artigos de vocês são bem didáticos!!
postado por cleriston em 06/07/2007 às 23:21
Aquele relacionamento da figura está correto mesmo? Não seria o contrário?
postado por bigo em 31/10/2007 às 23:21
ah nao... cabaciei
postado por bigo em 31/10/2007 às 23:21
Achei muito bom o artigo, porém não consegui visualizar como ficaria, em código, a inclusão de mais um departamento para um curso que já está incluso no banco. Não sei se vc me entendeu e até fico grato por um retorno, de qualquer forma o artigo é muito bom.
postado por Robson em 11/11/2007 às 23:21
Tentei rodar o exemplo acima e não funcionou.
postado por Victor em 06/12/2007 às 23:21
Google is the best search engine Google
postado por sLKlVYCSTw em 15/02/2008 às 23:21
Comente!

Observações

Os campos em negrito são obrigatórios.

Para evitar problemas, este espaço é moderado. Após o envio do comentário será necessário aguardar pela sua aprovação.