Hibernate Mapping: Mapeando Relacionamentos entre Entidades

Veja neste artigo como podemos fazer os relacionamentos entre entidades no Hibernate. Veja também quais são os principais atributos utilizados, quais são obrigatórios e quais são opcionais.

Hibernate facilita o armazenamento e a recuperação de objetos Java através do Mapeamento Objeto-Relacional (Object/Relational Mapping - ORM). O Hibernate oferece aos desenvolvedores a opção de criar mapeamentos entre modelos de base de dados e modelos de objetos através de duas formas: arquivos XML ou através de anotações no código fonte dos objetos. A opção preferível é a segunda.

Quando utilizamos anotações temos algumas vantagens como: código mais intuitivo do que arquivos baseados em XML, menos detalhes para se preocupar, facilidade de visualização das configurações, etc.

O Hibernate usa e suporta anotações JPA 2 que por sua vez suportam o mapeamento entre entidades. JPA 2 suporta as associações One-to-one (Um para Um), One-to-Many (Um para Muitos), Many-to-one (Muitos para Um) e Many-to-Many (Muitos para Muitos).

No restante do artigo estaremos estudando como podemos definir relacionamentos entre as nossas entidades.

1. Mapeando Associações One-to-One Embutidas

Neste relacionamento teremos os atributos das entidades relacionadas que serão persistidas na mesma tabela. Por exemplo, uma classe Pessoa que tem um relacionamento One-to-One com a classe endereço, conforme exemplo da Listagem 1.

Listagem 1. Definindo uma tabela com uma anotação @Embeddable

@Entity public class Pessoa { @Id private long id; private String nome; @Embedded private Endereco endereco; //get's e set's } @Embeddable public class Endereco { private String logradouro; //get's e set's }

Nesse caso temos todos os campos de uma tabela mantidos dentro de uma mesma tabela como se fosse outra tabela. Os atributos @Embedded e @Embeddable são usados para gerenciar este relacionamento. Uma entidade embutida deve ser composta inteiramente de campos e atributos básicos. As entidades embutidas usam as anotações @Basic, @Column, @Lob, @Temporal, e @Enumerated. Podemos observar que a chave-primária não pode ser mantida pela classe embutida e sim na classe que contém ela. A anotação @Embeddable não tem qualquer atributo adicional, ela é pura. A anotação @Embedded é utilizada para marcar campos ou métodos getter nas entidades que referencia a entidade embutida. A anotação @Embedded nos permite sobrescrever colunas através das tags @AttributeOverride e @AttributeOverrides.

No exemplo da Listagem 2 demonstramos como utilizar @AttributeOverride e @AttributeOverrides para sobrescrever nomes de coluna endereco e pais com os atributos ENDER e PAIS.

Listagem 2. Utilizando @AttributeOverride e @AttributeOverrides

@Embedded @AttributeOverrides({ @AttributeOverride(name="endereco",column=@Column(name="ENDER") ), @AttributeOverride(name="pais",column=@Column(name="PAIS")) }) public Endereco getEndereco() { return this.endereco; }

Como uma última dica, vale ressaltar que o Hibernate e JPA não suportam mapear um objeto embutido em mais de uma tabela.

2. Mapeando Associações One-to-One Convencionais

A anotação One-to-One é utilizada para associar duas entidades onde uma não é componente da outra, ao contrário da definição acima. Numa associação One-to-One também podemos ter um relacionamento bidirecional. Nesse caso, um dos lados precisará ser o dono do relacionamento e ser responsável por atualizar uma coluna com uma chave estrangeira. Para mais informações veja mais sobre o atributo mappedBy já discutido em outro artigo.

A aplicação do One-to-one é simples e possui apenas atributos opcionais. Na Listagem 3 temos um exemplo de como aplicar a anotação.

Listagem 3. Utilizando a anotação @OneToOne.

@OneToOne public Endereco getEndereco() { return this.endereco; }

Os atributos opcionais são os seguintes:

- targetEntity: é a classe da entidade que é o destino da associação. O default é o tipo do campo ou a propriedade que armazena a associação.

- cascade: pode ser configurado para qualquer um dos membros da enumeração javax.persistence.CascadeType.

- fetch: pode ser configurado para EAGER ou LAZY.

- optional: indica se o valor sendo mapeado pode ser null.

- orphanRemoval: indica que se o valor sendo mapeado é deletado, esta entidade também será deletada.

- mappedBy: indica que um relacionamento one-to-one bidirecional é apropriado pela entidade nomeada. O dono possui a chave-primária da entidade subordinada.

3. Mapeando Associações Many-to-One ou One-to-Many

A anotação @OneToMany pode ser aplicada para um campo ou propriedade de uma coleção ou um array representando o "many" da associação.

O atributo mappedBy é obrigatório numa associação bidirecional e opcional numa associação unidirecional. O atributo cascade também é opcional, possuindo um membro da enumeração javax.persistence.CascadeType. O atributo targetEntity também é opcional. Por fim, fetch também é opcional permitindo LAZY ou EAGER. Segue na Listagem 4 um exemplo da utilização.

Listagem 4. Utilizando mappedBy e cascade na anotação @OneToMany.

@OneToMany(cascade = ALL, mappedBy = "publicador") public Set<Livro> getLivros() { return livros; }

A anotação many-to-one deste relacionamento é expresso da mesma forma que a anotação anterior, conforme mostrado na Listagem 5.

Listagem 5. Utilizando a anotação @ManyToOne.

@ManyToOne @JoinColumn(name = "publicador_id") public Publicador getPublicador() { return publicador; }

@JoinColumn é utilizado para nomearmos a coluna que possui a chave-estrangeira requerida pela associação. Se nada for especificado, será utilizado o nome do campo.

Outra anotação bastante importante e utilizada é a @JoinTable que também é encontrada nos relacionamentos @ManyToMany.

Para exemplificar melhor podemos imaginar que tenhamos Usuarios e Perfis, ou seja, temos duas tabelas na nossa base de dados, sendo que cada usuário poderá ter apenas um perfil. Dessa forma, temos além das duas tabelas (Usuário e Perfil) uma terceira tabela para o mapeamento dos perfis para os usuários. Essa última tabela Usuários_Perfil é uma tabela intermediária que possui duas chaves estrangeiras para cada uma das tabelas Usuário e Perfil. Para mapear essa situação podemos usar o JoinTable. O exemplo da Listagem 6 mostra como ficaria uma classe Java mapeando o Usuario.

Listagem 6. Exemplificando a classe Usuario.

@Entity @Table(name="usuario") public class Usuario { @Id @GeneratedValue private Integer id; private String login; private String senha; @OneToOne(cascade=CascadeType.ALL) @JoinTable(name="usuario_perfil", joinColumns={@JoinColumn(name="usuario_id", referencedColumnName="id")}, inverseJoinColumns={@JoinColumn(name="perfil_id", referencedColumnName="id")}) private Perfil perfil; //getters e setters }

Na Listagem 7 temos o mapeamento da classe Perfil.

Listagem 7. Exemplificando a classe Perfil.

@Entity @Table(name="perfil") public class Perfil { @Id @GeneratedValue private Integer id; private String nomePerfil; @OneToMany(cascade=CascadeType.ALL) @JoinTable(name="usuario_perfil", joinColumns={@JoinColumn(name="perfil_id", referencedColumnName="id")}, inverseJoinColumns={@JoinColumn(name="usuario_id", referencedColumnName="id")}) private List<Usuario> usuarioList; //getters e setters }

A anotação @JoinTable indica que estamos interagindo com uma tabela intermediária, neste caso a tabela usuario_perfil, e no exemplo acima também configuramos o relacionamento e o mapeamento de colunas. joinColumns é resposnável pelo mapeamento de colunas do lado que é o dono. O atributo name possui o nome da coluna da tabela intermediária, referencedColumnName contém o nome da coluna chave-primária do lado que é dono (no nosso caso a tabela usuario possui como chave-primária id).

O atributo inverseJoinColumns é responsável por mapear colunas do lado inverso.

Para testar esse código, siga o exemplo da Listagem 8.

Listagem 8. Testando os códigos anteriores.

public class Teste { public static void main(String[] args) { SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); Session session = sessionFactory.openSession(); session.beginTransaction(); Perfil perfil = (Perfil) session.get(Perfil.class, 2); Usuario usuario = new Usuario("testeuser", "senhaqualquer"); usuario.setPerfil(perfil); session.save(usuario); session.getTransaction().commit(); session.close(); } }

Para simplificar ainda mais as ideias as tabelas, temos o exemplo da Listagem 9.

Listagem 9. Sqls utilizados nos exemplos.

CREATE TABLE `perfil` ( `id` int(6) NOT NULL AUTO_INCREMENT, `perfil` varchar(20) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; CREATE TABLE `usuario` ( `id` int(6) NOT NULL AUTO_INCREMENT, `login` varchar(20) NOT NULL, `senha` varchar(20) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; CREATE TABLE `usuario_perfil` ( `usuario_id` int(6) NOT NULL, `perfil_id` int(6) NOT NULL, KEY `usuario` (`usuario_id`), KEY `perfil` (`perfil_id`), CONSTRAINT `usuario` FOREIGN KEY (`usuario_id`) REFERENCES `usuario` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `perfil` FOREIGN KEY (`perfil_id`) REFERENCES `perfil` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

4. Mapeando Associações Many-to-Many

A anotação @ManyToMany tem os seguintes atributos:

- mappedBy: é o campo que indica o dono do relacionamento. Este atributo só é necessário quando a associação é bidirecional.

- targetEntity: é a classe da entidade que é o destino da associação.

- cascade: indica o comportamento em cascata da associação, o default é none (nenhum).

- fetch: indica o comportamento de busca da associação, sendo que o default é LAZY.

O exemplo da Listagem 10 demonstra a utilização desta anotação.

Listagem 10. Utilizando a anotação @ManyToMany.

@ManyToMany(cascade = ALL) public Set<Autor> getAutores() { return autores; }

Conclusão

Neste artigo vimos como mapear relacionamentos utilizando o Hibernate. Também vimos como omitir certos campos e quais são os principais e mais utilizados atributos para cada uma das anotações vistas.

Até a próxima!

Bibliografia

[1]Hibernate - JBoss Community, disponível em www.hibernate.org/

[2]Documentação de Referência Hibernate, disponível em https://docs.jboss.org/hibernate/core/3.6/reference/pt-BR/html/index.html

[3] Introdução ao Hibernate, disponível em http://www.hibernate.org/hib_docs/v3/reference/en/html/queryhql.html

[4] Jeff Linwood and Dave Minter. An introduction to persistence using Hibernate 3.5, Second Edition. Apress.

[5] Steve Perkins. Hibernate Search by Example: Explore the Hibernate Search system and use its extraordinary search features in your own applications. Packt Publishing.

Ebook exclusivo
Dê um upgrade no início da sua jornada. Crie sua conta grátis e baixe o e-book

Artigos relacionados