persistência de objetos através do framework hibernate

Propaganda
CURSO SUPERIOR DE TECNÓLOGIA EM
DESENVOLVIMENTO DE SOFTWARE
PERSISTÊNCIA DE OBJETOS
ATRAVÉS DO FRAMEWORK
HIBERNATE
FRANCISCO AUGUSTO FRANCO DO NASCIMENTO
CAMPOS DOS GOYTACAZES – RJ
2005
2
FRANCISCO AUGUSTO FRANCO DO NASCIMENTO
PERSISTÊNCIA DE OBJETOS ATRAVÉS DO FRAMEWORK
HIBERNATE: UMA VISÃO DO MAPEAMENTO OBJETORELACIONAL
Monografia apresentada ao Centro Federal de Educação
Tecnológica de Campos como requisito parcial para
conclusão do Curso Superior de Tecnologia em
Desenvolvimento de Software
Orientador: Profº Marcelo Machado Feres
Mestre em Engenharia de Software/ EMN-VUB
CAMPOS DOS GOYTACAZES – RJ
2005
3
Nascimento, Francisco Augusto Franco.
Persistência de objetos através do framework Hibernate /
Francisco Augusto Franco do Nascimento / Campos dos
Goytacazes/RJ, 2005.
65f.:
Monografia (Tecnologia em Desenvolvimento de Software) –
Centro Federal de Educação tecnológica de Campos, Campos
dos Goytacazes/ RJ.
Bibliografia: f. 78-80.
Orientador: Profº Marcelo Machado Feres.
1. Prática docente. 2. Procedimentos Metodológicos. 3.
Intervenção Social. I.Título.
4
Este trabalho, nos termos da legislação que resguarda os
direitos autorais, é considerado propriedade institucional.
É permitida a transcrição parcial de trechos do trabalho ou
menção ao mesmo para comentários e citações desde que não
tenha finalidade comercial e que seja feita a referência
bibliográfica completa.
Os
conceitos
expressos
responsabilidade do autor.
neste
trabalho
são
de
5
Monografia intitulada A prática do professor de literatura brasileira
sob a ótica dos alunos elaborada por Maria Emília Rocha e apresentada
publicamente perante a Banca Avaliadora, como parte dos requisitos para
conclusão do Curso de Licenciatura em Língua Portuguesa e Literatura Brasileira
da Universidade Santa Teresinha.
Aprovada em 22 de dezembro de 2005
Banca Avaliadora:
...................................................................................................................................
Profº Marcelo Machado Feres (orientador)
Mestre em Engenharia de Software/EMN-VUB
Centro Federal de Educação Tecnológica de Campos/RJ
...................................................................................................................................
Profª Aline Pires Vieira de Vasconcelos
Mestre em Engenharia de Software/ EMN-VUB
Centro Federal de Educação Tecnológica de Campos /RJ
...................................................................................................................................
Profº Maurício José Viana Amorim
Mestre em Sistemas e Computação/IME
Centro Federal de Educação Tecnológica de Campos /RJ
6
“Na natureza nada se
cria, nada se destrói, tudo se
transforma !” (Lavoisier)
7
DEDICATÓRIA
Dedico esta monografia para minha esposa
Glett, que com muito amor, carinho e paciência
me apoiou e não me deixou desanimar.
8
RESUMO
NASCIMENTO, Francisco Augusto Franco do. Persistência de objetos através do
framework Hibernate: uma visão do mapeamento objeto-relacional. Campos dos
Goytacazes,RJ: [s.n.], Centro Federal de Educação Tecnológica de Campos,
2005. Monografia (Tecnologia em Desenvolvimento de Software).
Palavras-chaves: persistência de objetos; mapeamento objeto-relacional;
framework Hibernate.
Neste trabalho, procurou-se fazer uma introdução a tecnologia
persistência de objetos em banco de dados relacionais. Para este fim, utilizou-se
a técnica de mapeamento objeto-relacional. Foi utilizando como exemplo de
estudo, o framework Hibernate. Através dele pode-se entender todas as fases
deste processo bem como, uma abordagem de utilização. Pode-se entender
como aplicar os conceitos da orientação a objetos, aplicados a persistência em
diferentes formas de configuração da solução.
9
ÍNDICE DE FIGURAS
Figura 1:
Visão geral da arquitetura de uma aplicação usando o Hibernate.....................
21
Figura 2:
Diagrama de Classes da Universidade............................................................... 31
Figura 3:
Diagrama de Tabelas do Banco de Dados (DED)..............................................
32
10
ÍNDICE DE LISTAGENS
Listagem 1: Script de Criação do Banco de Dados
33
Listagem 2: Listagem simplificada da classe Pessoa
39
Listagem 3: Endereco.hbm.xml - Mapeamento da classe Endereco
41
Listagem 4: Professor.hbm.xml - Mapeamento da classe Professor
44
Listagem 5: Aluno.hbm.xml - Mapeamento da classe Aluno
45
Listagem 6: Detalhe de Professor.hbm.xml - Mapeamento 1:N com a classe
46
Turma
Listagem 7: Exemplo de código mostrando a importância de utilizar o
47
“inverse”
Listagem 8: Exemplo de código mostrando a utilização de “set”
48
Listagem 9: Turma.hbm.xml - Mapeamento da classe Turma
49
Listagem 10: hibernate.cfg.xml – Configuração do HIbernate
51
Listagem 11: HibernateUtility.java – Classe para configurar e abrir sessões do 56
Hibernate
Listagem 12: Teste1.java – Teste de utilização do Hibernate para persistência 57
de uma instância da classe Curso
Listagem 13: Teste2.java – Teste para consultar dados de uma Turma
59
usando HQL
Listagem 14: Teste3.java – Teste para consultar dados de uma Turma
60
usando Criteria API
Listagem 15: Trecho para consultar as 10 primeiras turmas
61
Listagem 16: Trecho para consultar turmas usando parametro nomeado
61
Listagem 17: Trecho mostrando a utilização de “query” para consultas
62
externas
Listagem 18: Trecho mostrando a utilização de consultas externas nomeadas
63
11
SUMÁRIO
ÍNDICE DE FIGURAS ............................................................................................... IX
ÍNDICE DE LISTAGENS ............................................................................................ X
CAPÍTULO 1 – INTRODUÇÃO ................................................................................. 13
1.1 – Desenvolvimento de Aplicações OO........................................................ 13
1.2 – Java ......................................................................................................... 14
1.3 – Mapeamento Objeto-Relacional .............................................................. 14
CAPÍTULO 2 – PERSISTÊNCIA DE OBJETOS....................................................... 16
2.1 – Requisitos de Uma Camada de Persistência ........................................... 16
2.2 – Serialização ............................................................................................. 18
2.3 – Banco de Dados OO e Relacionais Estendidos ....................................... 19
2.4 – Banco de Dados Relacionais ................................................................... 20
2.5 – Padrões e Frameworks de Persistência em Java .................................... 21
2.5.1 – EJB CMP/CMR.................................................................................. 21
2.5.2 – JDO ................................................................................................... 22
2.5.3 – Framework Hibernate ........................................................................ 23
2.7 – Considerações Finais .............................................................................. 23
CAPÍTULO 3 – HIBERNATE: ARQUITETURA E CARACTERÍSTICAS ................. 24
3.1 – Arquitetura do Hibernate .......................................................................... 24
3.1.1 – Persistência e Eficiência ....................................................................... 25
3.1.2 – Características ...................................................................................... 27
Programação Orientada a Objetos ................................................................ 27
Suporte para Modelos de Objetos Específicos ............................................. 28
Sem Aumento de Tempo na Construção da Aplicação ................................. 28
Alta Escalabilidade ........................................................................................ 28
A Linguagem de Busca (Query Language) ................................................... 28
Gratuito e Aberto (Open Source) .................................................................. 28
Portabilidade ................................................................................................. 28
CAPÍTULO 4 – HIBERNATE: EXEMPLO DE UTILIZAÇÃO .................................... 30
4.1 – Descrição de um modelo de Universidade .............................................. 30
4.2 – Definindo os Objetos do Modelo .............................................................. 31
4.3 – Definido as Tabelas do Banco de Dados ................................................. 32
4.4 – Mapeamento das Tabelas para as Classes ............................................. 35
4.4.1 – Conceito de Identidade ..................................................................... 36
4.5 – Mapeamento de um Relacionamento 1:1 ................................................ 37
4.6 – Mapeamento de Herança ........................................................................ 41
4.7 – Mapeamento de Associações 1:N e N:N ................................................. 44
4.8 – Configuração do Hibernate 3 ................................................................... 48
CAPÍTULO 5 – FUNCIONAMENTO DO HIBERNATE ............................................. 51
5.1 – Realização de Pesquisas no Banco usando o Hibernate ........................ 54
5.2 – Restrições as Pesquisas.......................................................................... 59
CAPÍTULO 6 – CRIANDO UMA APLICAÇÃO COMPLETA USANDO O
HIBERNATE ............................................................................................................. 61
6.1 – Descrevendo o código da classe AplicacaoUniversidade ........................ 67
12
6.2 – Descrição da classe Leitor ....................................................................... 69
6.3 – Descrevendo o código da classe ListarCurso .......................................... 70
6.4 – Execução da aplicação ............................................................................ 72
6.5 – Recuperação das informações ................................................................ 74
6.6 – Conclusão ................................................................................................ 78
CAPÍTULO 7 – CONCLUSÃO .................................................................................. 79
8.1 – Considerações Finais .............................................................................. 80
REFERÊNCIAS BIBLIOGRÁFICAS: ........................................................................ 82
ANEXO ..................................................................................................................... 85
ANEXO I: HIBERNATE: INSTALAÇÃO ................................................................... 86
CAPÍTULO 1 – INTRODUÇÃO
1.1 – Desenvolvimento de Aplicações OO
O aumento da complexidade do software, de sua aplicação em diversos
setores da sociedade, a grande concorrência entre empresas de desenvolvimento
de software, todos estes fatores levaram a uma preocupação crescente com
vários aspectos do desenvolvimento de sistemas. Assim, questões relativas ao
tempo e custo de desenvolvimento, tecnologias utilizadas e adoção de
ferramentas de apoio tornaram-se decisivas para o sucesso de uma solução.
A adoção de boas práticas pregadas pela Engenharia de Software pode
auxiliar a atingir esses objetivos, possibilitando a construção de sistemas com um
menor custo, mais confiáveis e de boa qualidade. Atualmente, a principal prática
adotada pelas empresas de desenvolvimento de software para obter melhorias
em seus produtos é o paradigma da orientação a objetos (OO), através da
programação orientada a objetos (POO) e análise e projeto orientados a objetos
(APOO).
Tudo isto seria a solução definitiva se não fosse por um grande porém: As
aplicações legadas, que há muitos anos estão em produção, e que contemplam
anos e anos de dados armazenados. Em geral, elas são tão grandes e estão de
tal forma estáveis, que não seria viável re-desenvolver uma outra solução mais
atualizada para resolver o mesmo problema. Sem contar o fato, de que muitas
14
vezes, toda a estratégia de negócios da companhia pode estar sendo sustentado,
por tais aplicações. Neste contexto, todo desenvolvimento novo irá de alguma
forma, ter que se preocupar com as aplicações já existentes, seja para obter
insumo para o processamento, seja para descarregar a informação já processada.
A escolha de uma estratégia de solução, capaz de enfrentar este problema
e de ser duradoura, pode e deve ser um fator preponderante no processo
decisório. Visto que novas tecnologias, que pregam a solução de grandes
problemas, tendem a não manter a compatibilidade com o que já esta presente na
organização.
1.2 – Java
O trabalho adota a linguagem de programação Java, uma linguagem que
vem sendo amplamente empregada no desenvolvimento de software para
inúmeras aplicações e que inclui diversos conceitos e facilidades que refletem o
estado da arte em programação. Ela está disponível na maioria dos sistemas
operacionais, e nas mais importantes arquiteturas tais como Windows, Linux e
Solaris.
A linguagem de programação Java, por implementar fielmente vários
conceitos de orientação a objetos, como classes, herança, encapsulamento e
polimorfismo, naturalmente oferece um bom suporte para reuso e manutenção. O
Java possui um rico conjunto de bibliotecas e outras ferramentas para
desenvolvimento de programas, as quais encontram-se facilmente disponíveis
através da Internet, juntamente com uma extensa documentação sobre a
linguagem e sobre essas ferramentas.
1.3 – Mapeamento Objeto-Relacional
A introdução da orientação a objetos no contexto de desenvolvimento
provocou mudanças significativas na forma como os softwares são produzidos e
mantidos. O paradigma da orientação a objetos está focado em analogias a
coisas e comportamentos do mundo real, o que de fato facilita o desenvolvimento,
15
a manutenção e a evolução das aplicações melhorando a qualidade do produto
final.
Em conseqüência, muitas áreas do desenvolvimento de software estão
sendo revisadas, pois práticas, teorias e técnicas que eram adequadas, não
podem ser aplicadas de forma irrestrita, quando se trata de software OO. Uma
dessas áreas é a que trata da persistência das informações.
Inicialmente deve-se definir claramente o conceito de persistência.
Persistência é o que faz com que uma informação solicitada uma vez, esteja
disponível outras vezes na mesma aplicação, ou até mesmo para outras
aplicações. Adaptando-se ao universo dos objetos, as informações estão
distribuídas em objetos, então, persistência dos objetos é o que faz com que um
objeto solicitado uma vez, esteja disponível outras vezes na mesma aplicação, ou
até mesmo para outras aplicações.
Existem diversas alternativas que podem ser utilizadas para solucionar o
problema relativo à persistência dos objetos. Atualmente, a tecnologia mais
utilizada para esse fim são os Sistemas Gerenciadores de Bancos de Dados
Relacionais (SGBDR), ou simplesmente Bancos de Dados Relacionais.
Outrora no passado, SGBDR’s se posicionaram solidamente no mercado,
através de produtos robustos e seguros, com uma linguagem específica de
consultas e manipulação, o SQL (“Strutured Query Language”, ou seja,
Linguagem Estruturada de Consultas), que se tornou um padrão de mercado.
Segundo Alvim e Oliveira (2003), “os Sistemas Gerenciadores de Bancos de
Dados Relacionais conquistaram um lugar de destaque em comparação a outras
tecnologias de armazenamento de dados. Embora estes produtos realizem seu
papel de modo satisfatório no mundo para o qual foram criados, quando utilizados
no contexto OO adicionam complexidade extra, pois a aplicação passa a
necessitar de um processo intermediário de conversão” (Alvim & Oliveira, 2003)
[3], este processo é conhecido como mapeamento objeto-relacional.
CAPÍTULO 2 – PERSISTÊNCIA DE OBJETOS
“Persistência é a capacidade de um objeto de sobreviver fora dos limites da
aplicação que o criou” (Martins, V; 1999) [2]. Normalmente isto significa que o
objeto, ou melhor o estado dele, tem que ser gravado em um meio de
armazenamento persistente, como por exemplo o disco. Quando a aplicação
necessita novamente deste objeto, ela solicita que seu estado seja recuperado e
a partir disto, ele é novamente criado na memória, estando disponível para a
aplicação que o solicitou.
O ideal seria o uso de uma plataforma de desenvolvimento que permita
que o mesmo tipo de abstração possa ser aplicado em todas as etapas do ciclo
de vida do desenvolvimento de um software. Como esta ainda não é uma
possibilidade trivial, será feita uma analise das possibilidades mais comuns para
resolver esta questão, além da forma de implementar a persistência em uma
aplicação orientada a objetos.
2.1 – Requisitos de Uma Camada de Persistência
A camada de persistência deve oferecer os mecanismos necessários para
gravar permanentemente o estado dos objetos e recuperar novamente o mesmo
17
estado. Ela deve prover os serviços básicos de CRUD (create, read, update,
delete).

create() - Cria uma chave primária para o objeto e armazena ele no
banco.

read() - Diz ao objeto para recuperar os dados gravados.

update() - Grava qualquer alteração feita no objeto.

delete() - Remove o objeto do banco.
A implementação mais simples é fazer com que cada vez que a informação
de um objeto seja modificada, ela seja alterada no banco de dados. Este tipo de
persistência parece útil mas em sistemas grandes se torna ineficiente pois deixará
o sistema lento por consumir muitos recursos. O ideal é fazê-lo de tempos em
tempos.
A camada de persistência também é responsável por manter a segurança
dos dados, a integridade, a consistência, a performance, o travamento (locking) e
o suporte a transações.
Uma camada de persistência deve possuir:
1.
Serviços Básicos de CRUD – Incluir, remover, atualizar, etc.
2.
Encapsulamento completo dos mecanismos de persistência
– Apenas deve-se mandar mensagens como save, delete e
retrieve...
3.
Ações Multi-objetos – A camada de persistência deve dar
suporte à recuperação de vários objetos simultaneamente.
4.
Transações – A camada de persistência deve garantir que uma
ação deve ter sido feita com sucesso ou deverá fazer um roll
back.
5.
Capacidade de Extensão – Deve permitir adicionar novas
classes e deve permitir mudanças fáceis no mecanismo de
persistência.
6.
Identificadores de Objeto – Um número (OID) que identifica o
objeto como chaves primárias/estrangeiras deve ser gerado.
7.
Cursores – Um conceito que evita que centenas ou milhares de
objetos sejam recuperados de uma vez.
8.
Proxy – Um objeto que representa simplificadamente o objeto a
ser recuperado, evitando overhead.
18
9.
Registros – permitir retornar registros ao invés de objetos.
10.
Múltiplas arquiteturas – Permitir mudanças de arquiteturas
(centralizada para cliente/servidor, ou 2-tier, ou n-tier)
Várias versões de banco de dados – Habilidade para
11.
facilmente mudar os mecanismos de persistência para outro
fabricante ou versão de banco de dados.
Múltiplas conexões – Permitir conexões em vários bancos ao
12.
mesmo tempo.
Drivers nativos e não nativos – Acessar o banco de várias
13.
maneiras.
SQL queries – permitir usar diretamente códigos SQL.
14.
Dessa forma, considerando a persistência dos objetos, temos as seguintes
alternativas de solução:

Utilizar a serialização de objetos;

Utilizar banco de dados capaz de armazenar e recuperar objetos,
tais como banco de dados orientados a objetos e relacionais
estendidos;

Utilizar banco de dados relacional tradicional;
2.2 – Serialização
A opção mais simples de se fazer a persistência de um objeto é através da
serialização e deserialização de objetos. Este mecanismo permite que objetos
sejam
empacotados
com
informação
suficiente
que
permitam
posterior
reconstrução. A serialização permite que objetos sejam transformados em
Streams (Fluxos de dados, strings de caracteres) que por sua vez podem ser
transmitidos através da rede ou serem gravados em um meio de armazenamento.
Um objeto serializado é um grafo que inclui dados da classe e todas as
suas dependências. Isto é uma desvantagem muito grande porque se a classe ou
suas dependências mudarem, o formato usado na serialização mudará e os
novos objetos serão incompatíveis com os antigos, e não será mais possível
recuperar arquivos gravados com a versão antiga.
19
2.3 – Banco de Dados OO e Relacionais Estendidos
Os Sistemas Gerenciadores de Bancos de Dados Orientados a Objetos
(SGBDOO) fornecem transparência, pois não existe processo de conversão, já
que este já foi criado para persistir os objetos. Eles implementam de forma natural
os modelos orientados a objetos, isto é, os conceitos de classe, objeto, tipo,
identidade,
igualdade,
encapsulamento,
herança,
agregação,
método
e
polimorfismo (overloading, overriding e late binding). Além disso, possuem
mecanismos especiais para controlar transações entre objetos, técnicas de
armazenamento que facilitam a recuperação rápida de objetos complexos
(clustering) e funções adicionais para coordenar atividades cooperativas, tais
como mecanismos para avisar os usuários sobre mudanças de estado nos
objetos e para notificar a disponibilidade dos objetos. O acesso aos objetos é
mais versátil quando se utiliza um SGBDOO, já que é possível fazê-lo de forma
navegacional, ou seja, seguindo o grafo de objetos; ou por meio de linguagens do
tipo SQL.
Uma outra possível alternativa, os bancos de dados relacionais estendidos,
estes permitem a adição, a manipulação e a pesquisa eficiente de novos tipos de
dados à medida que eles se fazem necessários. Assim surgiram os sistemas
gerenciadores de bancos de dados objeto-relacional (SGBDOR), também
chamados SGBDR’s Estendidos.
Estender, neste contexto, significa adicionar tipos de dados definidos pelo
usuário (UDT’s – User-Defined Datatypes), funções definidas pelo usuário (UDF’s
– User-Defined Functions), métodos de acesso definidos pelo usuário e alterar o
otimizador de query que também deve ser extensível. Usuários e aplicações
simplesmente invocam UDF’s e não precisam entender sua estrutura interna.
É evidente que tanto um quanto o outro, estão mais preparados para fazer
o mapeamento objeto-relacional, de uma forma mais automátizada, capazes de
entender muitos tipos complexos como som e imagem. O importante é observar
que estas opções, embora disponíveis há algum tempo não conquistaram o
mercado, ocupando quase que um nicho muito específico. Isto aconteceu devido
20
a grande quantidade de dados armazenados em bancos de dados relacionais,
além do elevado grau de aceitação que os SGDBR´s conquistaram no mercado.
2.4 – Banco de Dados Relacionais
Uma solução para usar software OO e de manter a base relacional
instalada é a criação de classes ou framework de mapeamento objeto-relacional.
No entanto, a criação e manutenção dessas classes aumentam o trabalho de
codificação. Isto aumenta o risco de inserção de defeitos no software.
O modelo relacional está baseado em uma única estrutura de dados: a
relação. Uma relação é uma tabela com linhas (tuplas) e colunas (atributos), as
quais contêm um tipo de dado simples especificado (integer, character string,...).
Aqui começa o problema de impedância entre a tecnologia de objetos e os
bancos de dados relacionais. Os objetos podem ter estruturas complexas e, neste
caso, será necessário um esforço adicional de programação para adaptá-los ao
modelo relacional antes de armazená-los e outro para adequá-los novamente ao
modelo de objetos no momento de recuperá-los. Esses esforços estão totalmente
dissociados dos requisitos de negócio, trata-se de programação voltada à solução
de problemas tecnológicos.
As soluções geralmente adotadas envolvem descrever uma relação de
equivalencia entre os atributos do objeto e as colunas de uma ou mais tabelas no
banco relacional. Essa relação pode ser descrita através de código SQL, em que
a aplicação é responsável por persistir e recuperar os objetos diretamente no
banco. Quando o desenvolvedor opta por fazer a persistência "manualmente"
usando o código SQL, perde-se muito tempo desenvolvendo código-fonte para
recuperar e salvar os dados em banco e a aplicação fica muito vulnerável a
pequenos erros de programação típicos de trabalhos repetitivos e “copy-andpaste” (ou seja, copiar-e-colar). Além de se ter este requisito tecnológico
misturado com os requisitos do negócio, dificultando as manutenções e futuras
evoluções da aplicação. Compromete a flexibilidade, como por exemplo, a troca
de banco de dados, se necessário.
Alternativamente pode-se utilizar framework’s, ou camadas de persistência
capazes de realizarem o mapeamento objeto-relacional e vice-versa. Um
21
framework ou camada é “uma aplicação reusável, semicompleta que pode ser
especializada para produzir aplicações customizadas” (Martins, V; 1999) [2].
Estes frameworks visam automatizar esta tarefa, reduzindo o esforço de
integração entre estes dois mundos, o orientado a objetos e o relacional, através
de uma camada intermediária entre a aplicação (cliente/servidor ou web) e o
banco de dados, que permita acessar as informações do banco de dados sem
usar SQL e que, além disso, possibilite a troca por algum outro banco de dados
facilmente, sem a alteração no código da aplicação.
2.5 – Padrões e Frameworks de Persistência em Java
Tendo-se com base a linguagem Java, existem algumas alternativas de
camadas de persistência, que podem ser utilizadas para o desenvolvedor
implementar o mapeamento de objetos. Elas podem ser mais ou menos
complexas quanto ao seu funcionamento. Segundo Lozano [4], as principais
tecnologias para camada de persistência no Java são as seguintes:

EJB CMP/CMR (Enterprise JavaBeans, Container Managed
Persistence/ Container Managed Relationship);

JDO (Java Data Objects);

Framework Hibernate;
2.5.1 – EJB CMP/CMR
Para entender este mecanismo, é necessário primeiro entender o que é um
EJB. Um EJB é um componente do lado do servidor de aplicação que encapsula
a lógica de negócios de uma aplicação. Eles precisam ser construidos de acordo
com as especificações EJB e só são distribuídos e executados em um EJB
container.
O EJB container oferece serviços no nível de sistema, como transações,
para aplicativos EJB. Mas o interessante é um tipo de EJB chamado Entity Bean.
Um Entity Bean é um componente de dados que armazena permanentemente os
dados em uma estrutura secundária, geralmente um banco de dados.
22
Em um aplicativo EJB, um Entity Bean representa um registro de uma
tabela de banco de dados. Logo, ao invés de manipular dados diretamente no BD,
eles são manipulados nos Entity Beans.
O Container Managed Persistence é a criação automática de lógica de
persistência pelo servidor de aplicação. O container se encarrega de criar as
tabelas (no deployment), inserir os dados removê-los e alterá-los.
A CMP é utilizada na construção de aplicações totalmente independentes
de banco de dados. Ela tem uma API intrusiva, na qual o objeto a ser persistido
deve ser desenvolvido e códificado especificamente para este mecanismo.
Também se considera que um objeto persistente é antes disso, um componente
distribuído (remoto). Esta característica exige o uso de um servidor de aplicações.
A maioria das soluções CMP/CMR usa geração de código para implementar o
código de persistência. Ela é uma tecnologia bastante madura, com recursos
avançados de otimização e gerenciamento na maioria dos produtos disponíveis
no mercado.
Uma desvantagem da CMP/CMR é que ela tem uma curva de aprendizado
bastante longa. O padrão atual (2.1) peca por não especificar como é o
mapeamento OO/Relacional, gerando dependência em relação ao servidor de
aplicação
(descritores
proprietários).
Segundo
Lozano
[4],
uma
outra
desvantagem, é o fato de que ela “tem má fama no mercado, por causa de
limitações da versão 1.x, que não tinha recursos para relacionamentos entre
Entity Beans”.
A versão 3.0 é baseada no fato de que objetos persistentes não são
expostos para a camada de apresentação (cliente) no modelo MVC.
2.5.2 – JDO
Inicialmente foi criado como alternativa ao CMP, para aplicações que não
rodam no servidor de aplicações, embora ele também é bem-integrado em alguns
servidores de aplicações. Tem uma API não intrusiva, diferentes implementações
utilizam manipulação de bytecodes (bytecode decoration) ou pré-processamento.
O padrão atual peca por não definir o mapeamento OO/Relacional, mas o
problema será resolvido na versão 2.0 da especificação.
23
2.5.3 – Framework Hibernate
Não é padrão do JCP (Java Community Process), mas é quase um padrão
de fato do mercado. Ganhou muito espaço por causa do “preconceito” contra
EJBs e da padronização incompleta do CMP e do JDO. Devido a problemas
técnicos a maioria da JCP está a favor do Hibernate, ao invés do JDO.
A versão 3.0 está melhorando a documentação e recursos de otimização.
Ele usa reflexão e geração de bytecodes em tempo de execução, fazendo isto de
maneira mais transparente, a comunicação entre a aplicação e o banco de dados,
para o usuário final. Ele foi incorporado ao JBoss 4.0 como base do seu
mecanismo CMP/CMR. É famoso pela sua flexibilidade em termos de linguagem
de consulta e API.
2.7 – Considerações Finais
A tendência é que com o passar do tempo, o CMP/CMR ficará semelhante
ao Hibernate e ao JDO. A nova versão dele também terá um padrão para o
mapeamento objeto-relacional, compartilhado com o JDO 2.0
Neste trabalho optou-se por se estudar o Hibernate, por ser um projeto
OpenSource (ou seja, Código Aberto) e Free (ou seja, Licença Gratuita), sendo
que ele foi desenvolvido inteiramente em linguagem Java. Devido a este fato,
examinaremos mais a fundo este framework, que possui vantagens que seram
citadas em detalhes posteriormente, para assim entender quais as características
que o diferenciam dos seus concorrentes.
CAPÍTULO 3 – HIBERNATE: ARQUITETURA E
CARACTERÍSTICAS
Para desenvolver aplicações de forma mais produtiva, sem a necessidade
de realizar mapeamentos entre diferentes tipos de modelos (modelo de objetos x
modelo relacional), a tarefa de persistência deveria ser transparente ao
desenvolvedor. Este se preocuparia principalmente com a modelagem dos
objetos do domínio e sua codificação, enquanto os dados seriam gerenciados
através de uma camada de persistência de objetos.
O Hibernate é um framework para realizar o mapeamento objeto-relacional
em Java. Ele transforma os dados tabulares de um banco de dados em um grafo
de objetos definido pelo desenvolvedor. Com isso, ele se livra de escrever todo o
código de acesso ao banco de dados e de SQL, acelerando a velocidade do
desenvolvimento.
3.1 – Arquitetura do Hibernate
O Hibernate constitui-se de uma biblioteca de classes ou framework de
código-aberto (open source) para mapeamento objeto-relacional, que foi criado
para Java, desenvolvido totalmente em Java, se colocando como uma camada
25
adicional entre a aplicação e o banco de dados. Seu objetivo é prover uma
camada que permita a utilização de um mecanismo de persistência de objetos,
realizando assim o mapeamento automático entre o modelo relacional e o modelo
de classes da aplicação. Sendo assim, eliminando a existência de instruções SQL
nas classes do domínio.
3.1.1 – Persistência e Eficiência
O projeto Hibernate foi criado para reduzir o tempo que o programador
gasta em suas tarefas relacionadas à persistência de dados no desenvolvimento
de um software orientado a objetos, interagindo com um banco de dados
relacional.
Ele proporciona uma solução aos ambientes Java de mapeamento
objeto/relacional, abstraindo toda a representação de tipo de dados através de
arquivos XML (Extensible Markup Language). A estrutura de uma aplicação
desenvolvida com o Hibernate é mostrada na Figura 1.
Através dessa abstração, o Hibernate se torna responsável por gerar o
SQL necessário à aplicação, poupando o trabalho manual de programação da
conversão dos dados relacionais em objetos.
26
Figura 1: Visão geral da arquitetura de uma aplicação usando o Hibernate
A idéia é tornar totalmente transparente para o desenvolvedor o acesso ao
banco de dados. Por exemplo, uma classe chamada Cliente mapeada em uma
tabela chamada Clientes, não se vai trabalhar diretamente com a tabela e nem se
vai abrir conexão com o banco de dados. Na verdade, devem ser realizados três
passos:
1) Mapear a tabela em um arquivo XML utilizado pelo Hibernate (XML
mapping – Figura 1);
2) Gerar as classes baseadas nos arquivos XML das tabelas;
3) Usar as rotinas do Hibernate para as operações de busca, cadastro
ou alteração de dados.
Para cadastrar um novo cliente, não é realizada a operação de INSERT, e
sim alterados os valores da classe Cliente que fará uso automaticamente das
rotinas do Hibernate.
A princípio parece mais trabalhoso, mas na realidade é muito prático e
produtivo trabalhar usando meia dúzia de linhas de código do Hibernate, a ficar
chamando via seu código, rotinas de abrir conexão na base, ou carregar os
valores de uma linha em um atributo, etc.
27
Na figura 1, dois componentes sobressaem:

Hibernate.properties;

XML mapping.
O hibernate.properties é o arquivo que contém as informações sobre a
conexão com o banco de dados. Nele definimos o tipo do banco, dialeto SQL do
banco (dialect), driver, url, nome do usuário de conexão (username) e senha de
conexão (password). O Hibernate suporta muitos SGBD´s. Para facilitar o
processo de configuração do arquivo hibernate.properties, são fornecidos
modelos que devem ser customizados pelo desenvolvedor.
No XML mapping são registradas as informações a respeito do
mapeamento das classes para suas respectivas tabelas relacionais. Cada classe
é mapeada para uma ou mais tabelas. Além disso, são registradas informações
sobre os relacionamentos, cardinalidades e identificadores, entre outras.
Vale notar que as classes do Hibernate realizam a leitura desses arquivos
em tempo de execução, permitindo alterações sem a necessidade de
recompilação.
3.1.2 – Características
Existem seis principais características que dão destaque ao Hibernate.
Estas características são apresentadas em mais detalhes a seguir:
Programação Orientada a Objetos
O Hibernate suporta características de uma linguagem orientada a objetos
como herança (de várias maneiras), associação (um-para-um ou um-para-muitos
e agregação) e composição. Isso ajuda muito no desenvolvimento, pois o
programador não precisa se preocupar com o mapeamento para uma base
relacional. Podemos Implementar uma classe pai Pessoa que tem como filhos as
classes PessoaFisica e PessoaJurídica e o Hibernate faz o mapeamento desses
dados.
28
Suporte para Modelos de Objetos Específicos
O Hibernate possui uma rica variedade de mapeamentos para variáveis
compostas do Java: Map, Set, List, SortedMap, SortedSet, array e Collection.
Sem Aumento de Tempo na Construção da Aplicação
Não existe geração de código a mais ou processamento a mais para
construir sua aplicação pelo fato de estar utilizando o framework. Ou seja,
quando o seu software é compilado, não é necessária a recompilação do
Hibernate. Alguns frameworks necessitam ser recompilados junto com a aplicação
para funcionar.
Alta Escalabilidade
O Hibernate possui uma ótima performance, com uma arquitetura de cache
de duas camadas, podendo ser usado até em cluster.
A Linguagem de Busca (Query Language)
O hibernate soluciona ambos os problemas: não só como buscar os
objetos do banco de dados, mas também como popular dados. O Hibernate é
equipado com uma poderosa linguagem de busca (o HQL, Hibernate Query
Language) que intensionalmente é muito parecida com o SQL, mas é mais
orientada a objetos, facilitando os programadores Java que não estão
familiarizados com o SQL.
Gratuito e Aberto (Open Source)
O Hibernate é licenciado pela LGPL (Lesser GNU Public License).
Portabilidade
O Hibernate é portável para todos os bancos compatíveis com o padrão
SQL. Isso significa que sua aplicação pode ser desenvolvida usando um
29
inicialmente um banco de dados e depois, com alguns ajustes nos arquivos do
Hibernate (e não no código), ser portada para outro banco de dados relacional, tal
como Oracle, DB2, MySQL, PostgreSQL, Sybase, SAP DB, HypersonicSQL,
Microsoft SQL Server, Progress, Mckoi SQL, Pointbase, Interbase, etc.
CAPÍTULO 4 – HIBERNATE: EXEMPLO DE UTILIZAÇÃO
Para se avaliar melhor as capacidades do Hibernate e os procedimentos
necessários para a construção de uma camada de persistência, a melhor
alternativa é utilizar-se de um modelo simplificado como exemplo. De tal forma, a
maioria das atividades básicas do desenvolvedor podem ser detalhadas,
facilitando a compreensão do processo em que elas estão inseridas .
4.1 – Descrição de um modelo de Universidade
Uma universidade quer um sistema para controlar sua parte docente e
discente. O sistema deverá controlar os seguintes processos:

Montagem da grade de disciplina dos cursos, com cada disciplina
sua ementa;

Criação das turmas por disciplinas;

Matrícula do aluno na turma;

Alocação do professor na turma;

Atualização de endereços (rua, número, complemento, bairro,
cidade, estado, CEP) alunos e professores. As informações que se
deseja controlar são nome, telefone, e-mail;
31
4.2 – Definindo os Objetos do Modelo
O modelo é simples, ele modela um cadastro de alunos em uma
universidade. Nele, nós temos as classes:

Pessoa;

Aluno;

Professor;

Endereço;

Turma;

Disciplina;

Curso;
As classes têm os seguintes atributos:

Uma Pessoa tem nome, email, telefone;

Um Aluno tem matricula;

Um Professor tem título;

Cada Turma tem um nome;

Cada Disciplina tem um nome e uma ementa;

Cada Curso tem um nome e uma descrição;
As classes se relacionam da seguinte forma:

A classe Pessoa, que tem um Endereço, é a classe base para as
classes Aluno e Professor;

Um Aluno pode estar em várias Turmas;

Cada Turma pode ter vários Alunos e apenas um Professor;

Em uma Turma é lecionada uma Disciplina;

Uma Disciplina pode ser lecionada em várias Turmas e é de um
único Curso;

Em Curso pode ser composto por várias Disciplinas;
Baseados em todas esta informações, criamos um modelo de classes
ilustrado no diagrama de classes a seguir:
32
Figura 2: Diagrama de Classes da Universidade
4.3 – Definido as Tabelas do Banco de Dados
Figura 3: Diagrama de Tabelas do Banco de Dados (DED)
Como se pode perceber, o banco de dados criado para o modelo não
apresenta quaisquer características especiais, sendo modelado o mais simples e
convencional possível, adaptado para comportar um modelo de classes. A seguir,
33
a listagem do script para criação do banco de dados no MySQL. Para executá-lo
utilize o MySQL Query Browser.
Primeiro abre o MySQL Query Browser, crie um banco de dados chamado
“hibernate”. Depois de feito isto, ative o banco e digite o script de banco, e
escolha “Execute”;
CREATE TABLE Pessoa (
id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
nome VARCHAR(255) NULL,
email VARCHAR(255) NULL,
telefone VARCHAR(255) NULL,
PRIMARY KEY(id)
);
CREATE TABLE Curso (
id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
nome VARCHAR(255) NULL,
descricao TEXT NULL,
PRIMARY KEY(id)
);
CREATE TABLE Professor (
Pessoa_id INTEGER UNSIGNED NOT NULL,
titulo VARCHAR(255) NULL,
PRIMARY KEY(Pessoa_id),
INDEX Professor_FKIndex1(Pessoa_id),
FOREIGN KEY(Pessoa_id)
REFERENCES Pessoa(id)
ON DELETE NO ACTION
ON UPDATE NO ACTION
);
CREATE TABLE Endereco (
Pessoa_id INTEGER UNSIGNED NOT NULL,
rua VARCHAR(255) NULL,
numero INTEGER UNSIGNED NULL,
bairro VARCHAR(255) NULL,
estado VARCHAR(255) NULL,
complemento TEXT NULL,
cep VARCHAR(255) NULL,
cidade VARCHAR(255) NULL,
PRIMARY KEY(Pessoa_id),
INDEX Endereco_FKIndex1(Pessoa_id),
FOREIGN KEY(Pessoa_id)
REFERENCES Pessoa(id)
ON DELETE NO ACTION
ON UPDATE NO ACTION
);
Listagem 1: Script de Criação do Banco de Dados
34
CREATE TABLE Aluno (
Pessoa_id INTEGER UNSIGNED NOT NULL,
matricula VARCHAR(255) NULL,
PRIMARY KEY(Pessoa_id),
INDEX Aluno_FKIndex1(Pessoa_id),
FOREIGN KEY(Pessoa_id)
REFERENCES Pessoa(id)
ON DELETE NO ACTION
ON UPDATE NO ACTION
);
CREATE TABLE Disciplina (
id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
Curso_id INTEGER UNSIGNED NOT NULL,
nome VARCHAR(255) NULL,
ementa TEXT NULL,
PRIMARY KEY(id),
INDEX Disciplina_FKIndex1(Curso_id),
FOREIGN KEY(Curso_id)
REFERENCES Curso(id)
ON DELETE NO ACTION
ON UPDATE NO ACTION
);
CREATE TABLE Turma (
id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
Disciplina_id INTEGER UNSIGNED NOT NULL,
Professor_Pessoa_id INTEGER UNSIGNED NOT NULL,
nome VARCHAR(255) NULL,
PRIMARY KEY(id),
INDEX Turma_FKIndex1(Professor_Pessoa_id),
INDEX Turma_FKIndex2(Disciplina_id),
FOREIGN KEY(Professor_Pessoa_id)
REFERENCES Professor(Pessoa_id)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
FOREIGN KEY(Disciplina_id)
REFERENCES Disciplina(id)
ON DELETE NO ACTION
ON UPDATE NO ACTION
);
CREATE TABLE Turma_has_Aluno (
Turma_id INTEGER UNSIGNED NOT NULL,
Aluno_Pessoa_id INTEGER UNSIGNED NOT NULL,
PRIMARY KEY(Turma_id, Aluno_Pessoa_id),
INDEX Turma_has_Aluno_FKIndex1(Turma_id),
INDEX Turma_has_Aluno_FKIndex2(Aluno_Pessoa_id),
FOREIGN KEY(Turma_id)
REFERENCES Turma(id)
Como
você já
ter percebido, não há nada demais com esse modelo
ON DELETE
NOdeve
ACTION
ON de
UPDATE
NO as
ACTION,
de banco
dados,
tabelas estão interligadas normalmente usando chaves
FOREIGN KEY(Aluno_Pessoa_id)
REFERENCES
Aluno(Pessoa_id)
primárias
e estrangeiras
e uma tabela de relacionamento, no caso da relação N:N
ON DELETE NO ACTION
ON UPDATE
NO As
ACTION
entre Aluno
e Turma.
tabelas têm as mesmas propriedades das classes e os
);
mesmos relacionamentos.
Listagem 1: Script de Criação do Banco de Dados (continuação)
35
Quando estiver mapeando as suas classes do modelo para o banco de
dados ou vice versa, tente usar os mesmos nomes das classes e de suas
propriedades, isso vai evitar várias dores de cabeça, como não saber se o nome
“daquela” propriedade é todo em maiúsculas, ou se tem um “_” entre um nome e
outro. Além do que, com veremos a seguir, se os nomes forem iguais, você não
precisará repetir os nomes das tabelas e dos relacionamentos no mapeamento do
Hibernate.
4.4 – Mapeamento das Tabelas para as Classes
O próximo passo é a criação dos arquivos XML de mapeamento. O arquivo
é identificado pelo nome da classe mais a extensão .hbm.xml. A criação/edição
desses arquivos pode ser feita manualmente mas também poder ser feita
utilizando uma ferramenta própria para este fim. Neste tutorial, porém nós não
utilizaremos nenhuma ferramenta específica para este fim.
Caso deseje utilizar algumas destas ferramentas descrevemos a seguir
uma lista das mais comuns:

HiberClipse - É um plugin para o Eclipse que permite a geração de
arquivos de mapeamento Hibernate de uma conexão com o banco
de dados;

HibernateSynchronizer – É um plugin para o Eclipse totalmente
feito
em
Java,
que
se
associa
aos
arquivos
*.hbm
e
automaticamente gera suas classes de domínio quando se arquivo
de configuração de esquema (schema) do Hibernate é modificado;

Hibernator – É um plugin para o Eclipse totalmente feito em Java,
que permite a sincronização de uma classe em Java e um arquivo
de mapeamento Hibernate. O usuário pode ter alterado a classe de
domínio e não ter atualizado o mapeamento.

Middlegen – É um mecanismo gerador de código orientado a banco
de dados gratuito de propósito geral baseado em JDBC, Velocity,
Ant and Xdoclet;

Hibernate Tools – É um plugin para o Eclipse totalmente feito em
Java, que permite a sincronização de uma classe em Java e um
arquivo de mapeamento Hibernate;
36
Além destas, com certeza existem várias outras ferramentas criadas com
esta finalidade, se desejar mais informação sobre este tipo de ferramenta, nós
recomendamos pesquisar na Internet. Maiores detalhes sobre isto fogem ao foco
deste tutorial, que não pretendemos prender a nenhuma ferramenta e ao
contrário, permitir que cada um utiliza-o do modo que acreditar se mais cômodo.
Independente se os arquivos forem gerados ou criados manualmente,
podemos derivar novos arquivos apenas realizando as modificações necessárias
sobre a primeira versão. Por exemplo, se iniciarmos um novo projeto e estivermos
utilizando as classes herdadas de Pessoa e Endereço do nosso modelo,
reutilizaremos
boa parte
da
estrutura
dos arquivos
Pessoa.hbm.xml
e
Endereco.hbm.xml para o novo mapeamento.
4.4.1 – Conceito de Identidade
Antes de começar a fazer os mapeamentos do Hibernate, temos um
conceito do banco de dados que precisa ser revisto, a identidade. Para um banco
de dados, o modo de diferenciar uma linha das outras, é usando chaves
primárias, de preferência chaves não naturais, como as colunas “id” que nós
criamos para nossas tabelas, mas em nosso modelo orientado a objetos, a
identidade não é encontrada dessa forma. Em Java, nós definimos a identidade
dos objetos sobrescrevendo o método “Object.equals (Object)”, do modo que nos
convier. A implementação “default” deste método, define a identidade através da
posição de memória ocupada pelo objeto.
Não podemos usar o método “equals()” no banco de dados, porque ele não
sabe que temos objetos, ele só entende tabelas, chaves primárias e estrangeiras.
A solução é adicionar aos nossos objetos um identificador não natural, como os
que nós encontramos no banco de dados, porque assim o banco de dados e o
próprio Hibernate vão ser capazes de diferenciar os objetos e montar os seus
relacionamentos. Nós fazemos isso adicionando uma propriedade chamada “id”
do tipo Integer a todas as nossas classes, como no exemplo de código a seguir:
37
//Listagem do arquivo Pessoa.java
public class Pessoa {
private String nome;
private String email;
private String telefone;
private Endereco endereco;
private Integer id;
//métodos getters e setters das propriedades
}
Listagem 2: Listagem simplificada da classe Pessoa
Poderíamos ter escolhido o tipo primitivo “int” para a propriedade, mas
trabalhando com objetos, o mapeamento e a resolução se um objeto existe ou
não no banco de dados torna-se mais simples para o Hibernate. Outra
observação importante são os métodos “getters” e “setters” para as propriedades
dos objetos. O Hibernate não precisa de que as propriedades sejam definidas
usando a nomenclatura dos JavaBeans, mas isso já é uma boa prática comum na
comunidade, além de existirem outros frameworks e tecnologias que exigem essa
nomenclatura (como a Expression Language dos JSPs), então nós definimos
métodos “getters” e “setters” para todas as propriedades nos nossos objetos.
4.5 – Mapeamento de um Relacionamento 1:1
O primeiro mapeamento abordado é o mapeamento da classe Pessoa e do
seu relacionamento com a classe Endereço. No nosso modelo, um Endereço tem
apenas uma Pessoa, e uma Pessoa tem apenas um Endereço.
Na listagem 2, podemos conferir o arquivo Pessoa.hbm.xml que faz o
mapeamento para a classe Pessoa.
38
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD
3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="Pessoa">
<!-- Identificador da classe -->
<id name="id">
<generator class="increment"/>
</id>
<!-- Propriedades da classe -->
<property name="nome"/>
<property name="telefone"/>
<property name="email"/>
<!-- Relacionamento da classe -->
<one-to-one
name="endereco"
class="Endereco"
cascade="save-update"/>
</class>
</hibernate-mapping>
Listagem 2: Pessoa.hbm.xml - Mapeamento da classe Pessoa
O arquivo de mapeamento é um arquivo XML que define as propriedades e
os relacionamentos de uma classe para o Hibernate, este arquivo pode conter
classes, classes componentes e queries em HQL ou em SQL. No nosso exemplo,
temos apenas uma classe sendo mapeada no arquivo, a classe Pessoa.
O arquivo XML começa normalmente com as definições da DTD e do nó
raiz, o <hibernate-mapping>, depois vem o nó que nos interessa neste caso,
<class>.
No nó <class> nós definimos a classe que está sendo mapeada e para qual
tabela ela vai ser mapeada. O único atributo obrigatório deste nó é “name”, que
deve conter o nome completo da classe (com o pacote, se ele não tiver sido
definido no atributo “package” do nó <hibernate-mapping>), se o nome da classe
for diferente do nome da tabela, você pode colocar o nome da tabela no atributo
“table”, no nosso exemplo isso não é necessário.
39
Seguindo em frente no nosso exemplo, temos o nó <id> que é o
identificador dessa classe no banco de dados. Neste nó nós definimos a
propriedade que guarda o identificador do objeto no atributo “name”, que no nosso
caso é “id”, se o nome da coluna no banco de dados fosse diferente da
propriedade do objeto, ela poderia ter sido definida no atributo “column”. Ainda
dentro deste nó, nós encontramos mais um nó, o <generator>, este nó guarda a
informação de como os identificadores (as chaves do banco de dados) são
gerados, existem diversas classes de geradores, que são definidas no atributo
“class” do nó, no nosso caso o gerador usado é o “increment”, que incrementa um
ao valor da chave sempre que insere um novo objeto no banco, esse gerador
costuma funcionar normalmente em todos os bancos.
Os próximos nós do arquivo são os <property> que indicam propriedades
simples dos nossos objetos, como Strings, os tipos primitivos (e seus wrappers),
objetos Date, Calendar, Locale, Currency e outros. Neste nó, os atributos mais
importantes são “name”, que define o nome da propriedade, “column” para
quando a propriedade não tiver o mesmo nome da coluna na tabela e “type” para
definir o tipo do objeto que a propriedade guarda. Normalmente, o próprio
Hibernate é capaz de descobrir qual é o tipo de objeto que a propriedade guarda,
não sendo necessário escrever isso no arquivo de configuração, ele também usa
o mesmo nome da propriedade para acessar a coluna, se o atributo não tiver sido
preenchido. Nós definimos as três propriedades simples da nossa classe, “nome”,
“email” e “telefone”.
O último nó do arquivo, <one-to-one>, define o relacionamento de 1-para-1
que a classe Pessoa tem com a classe Endereço. Este nó tem os atributos
“name”, que define o nome da propriedade no objeto (neste caso, “endereco”),
“type” que define a classe da propriedade e “cascade” que define como o
Hibernate deve “cascatear” as ações feitas nesta classe para a classe
relacionada, como atualizações, inserções e exclusões de registro. No nosso caso
o “cascade” foi escolhido como “save-update” para que quando uma classe
pessoa for inserida ou atualizada no banco, a propriedade “endereco” também
seja inserida ou atualizada.
Com isso, terminamos o mapeamento da nossa classe Pessoa, mas este
mapeamento faz referência a uma outra classe o Hibernate ainda não conhece, a
40
classe Endereco. Vejamos então como fica o mapeamento desta classe, neste
caso, o arquivo é o “Endereco.hbm.xml”:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="Endereco">
<id name="id"
column="Pessoa_id">
<generator class="foreign">
<param name="property">pessoa</param>
</generator>
</id>
<property
<property
<property
<property
<property
<property
<property
name="bairro"/>
name="cidade"/>
name="complemento"/>
name="estado"/>
name="numero"/>
name="rua"/>
name="cep"/>
<one-to-one
name="pessoa"
class="Pessoa"
constrained="true"/>
</class>
</hibernate-mapping>
Listagem 3: Endereco.hbm.xml - Mapeamento da classe Endereco
Como você agora já conhece a estrutura básica do arquivo de
mapeamento,
vejamos
as
diferenças
deste
arquivo
para
o
arquivo
“Pessoa.hbm.xml”. A primeira coisa que você deve estar notando, é que o atributo
“class” do nó <generator> não é “increment”, é “foreign” e agora também temos
um corpo do nó, com o valor “pessoa”. Isso acontece devido ao tipo de
relacionamento que nós criamos no banco de dados, entre as tabelas Endereco e
Pessoa. Também existe mais um nó dentro de <generator> que não existia no
mapeamento anterior, o <param>, esse nó serve para passar um parâmetro para
a classe geradora do identificador, que neste caso é o nome da propriedade
“dona” deste objeto, “pessoa”.
41
Para garantir que cada Endereco, pertença a apenas uma Pessoa, nós
fizemos com que a chave primária de Endereco (Pessoa_id) fosse também a
chave estrangeira que a liga a Pessoa, desse modo, garantimos que cada
Endereco pertence a apenas uma Pessoa e vice-versa. Outra novidade é que
também tivemos que colocar o nome da coluna da chave já que ela não é igual a
o nome da propriedade da classe, a propriedade se chama “id” e a coluna
“Pessoa_id”, assim, tivemos que declarar o atributo “column” do nó <id>.
Continuando no mapeamento, encontramos as propriedades simples da
classe, que são declaradas de modo idêntico as que nós vimos no mapeamento
anterior, com o nó <property> e com o atributo “name” contendo o nome da
propriedade. Mais uma vez, nós não indicamos o nome da coluna (porque os
nomes são iguais as propriedades) nem o tipo, já que o Hibernate pode descobrir
isso sozinho.
No nosso modelo, o relacionamento entre Pessoa e Endereco é
bidirecional, o que quer dizer que é sempre possível navegar de um objeto para o
outro, pois Pessoa aponta para Endereco e Endereço aponta para Pessoa. Para
simbolizar isso no mapeamento, nós temos que adicionar o nó que também existe
no mapeamento de Pessoa, <one-to-one>.
Como você já percebeu, os atributos continuam os mesmos, “name” como
sendo o nome da propriedade na classe e “class” como sendo o nome da classe
dessa propriedade. Mas nós temos um atributo que não existia na relação
anterior, “constrained”, que nessa relação vai significar que existe uma relação
entre a chave primária de Endereco e de Pessoa, avisando ao Hibernate que um
Endereco não pode existir sem que exista uma Pessoa, assim, mesmo que o
banco de dados não garantisse a integridade referencial do sistema, o próprio
Hibernate garantiria.
4.6 – Mapeamento de Herança
Continuando o trabalho de mapear o nosso modelo para o Hibernate,
vamos para a herança que nós encontramos entre Pessoa, Aluno e Professor.
Pessoa é a classe pai, da qual Aluno e Professor herdam as propriedades e
comportamentos.
42
No Hibernate existem diversas formas de se fazer o mapeamento de uma
relação de herança. Usando uma tabela para cada classe filha (a classe pai não
teria tabela, as propriedades comuns se repetiriam nas tabelas filhas), usando
uma tabela para todas as classes (discriminadores definiriam quando é uma
classe ou outra), usando uma tabela para cada uma das classes (uma para a
classe pai e mais uma para cada classe filha) ou até mesmo uma mistura de
todas essas possibilidades (disponível apenas na versão 3 do Hibernate). Ficou
confuso? Não se preocupe, neste artigo nós vamos abordar apenas a mais
comum e mais simples de ser mantida, uma tabela para cada classe.
No nosso banco de dados, temos uma tabela Pessoa e outras duas
tabelas, Aluno e Professor, que estão relacionadas a professor través de suas
chaves primárias a tabela Pessoa, garantindo assim que não existam Alunos ou
Professores com o mesmo identificador. Vejamos então o mapeamento da classe
Professor, o arquivo é o “Professor.hbm.xml”:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<joined-subclass name="Professor" extends="Pessoa">
<key column="Pessoa_id"/>
<property name="titulo"/>
<set name="turmas"
inverse="true">
<key column="Pessoa_Professor_id"/>
<one-to-many class="Turma"/>
</set>
</joined-subclass>
</hibernate-mapping>
Listagem 4: Professor.hbm.xml - Mapeamento da classe Professor
Nesse mapeamento vemos um nó que nós ainda não conhecíamos,
<joined-subclass>, que indica o mapeamento de herança usando uma tabela para
cada classe, porque para retornar o objeto o Hibernate precisa fazer um “join”
entre as duas tabelas. O atributo “name” é o nome da classe mapeada e o
43
atributo “extends” recebe o nome da classe pai (neste caso, Pessoa), se o nome
da classe não fosse igual ao da tabela, poderíamos adicionar o nome da tabela no
atributo “table” (que foi omitido porque o nome da classe é igual ao nome da
tabela).
Seguindo no mapeamento, percebemos mais um nó desconhecido, <key>.
Este nó avisa ao Hibernate o nome da coluna que guarda a chave primária da
tabela, esta coluna também é a chave estrangeira e que liga a tabela Professor a
tabela Pessoa. Neste nó nós adicionamos o atributo “column” e colocamos o
nome da coluna que guarda a chave, que é “Pessoa_id”.
O mapeamento continua com a declaração de uma propriedade e com um
nó que nós também não conhecemos, <set>, que pode simbolizar um
relacionamento 1:N ou N:N. Este nó vai ser explicado mais tarde neste artigo.
Passando para o mapeamento da classe Aluno, percebemos que não
existe muita diferença, vejamos o arquivo “Aluno.hbm.xml”:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<joined-subclass name="Aluno" extends="Pessoa">
<key column="Pessoa_id"/>
<property name="matricula"/>
<set name="turmas"
table="Turma_has_Aluno"
inverse="false">
<key column="Aluno_Pessoa_id"/>
<many-to-many class="Turma" column="Turma_id"/>
</set>
</joined-subclass>
</hibernate-mapping>
Listagem 5: Aluno.hbm.xml - Mapeamento da classe Aluno
44
A classe é declarada do mesmo modo que Professor, usando o nó <joinedsubclass> e usando os mesmos atributos que foram usados no mapeamento
anterior. O nó <key> também foi incluído do mesmo modo, com o nome da coluna
da chave primária/estrangeira da tabela, mais uma declaração de propriedade e
mais um nó <set>, nada além do que já foi visto.
4.7 – Mapeamento de Associações 1:N e N:N
Você já conheceu o nó <set> nos mapeamentos anteriores, vamos
entender agora como ele funciona e como utilizá-lo para tornar o acesso aos
objetos associados ainda mais simples, mais uma vez sem nenhuma linha de
SQL. Um “set” ou “conjunto” representa uma coleção de objetos não repetidos,
que podem ou não estar ordenados (dependendo da implementação escolhida da
interface java.util.Set). Quando você adiciona um nó deste tipo em um arquivo de
mapeamento, você está indicando ao Hibernate que o seu objeto tem um
relacionamento 1:N ou N:N com outro objeto. Vejamos o exemplo da classe
Professor, que tem um relacionamento 1:N com a classe Turma:
<joined-subclass name="Professor" extends="Pessoa">
<key column="Pessoa_id"/>
<property name="titulo"/>
<set name="turmas"
inverse="true">
<key column="Pessoa_Professor_id"/>
<one-to-many class="Turma"/>
</set>
</joined-subclass>
Listagem 6: Detalhe de Professor.hbm.xml - Mapeamento 1:N com a classe Turma
No nó <set> o primeiro atributo a ser encontrado é “name” que assim como
nos outros nós, define o nome da propriedade que está sendo tratada, já o outro
atributo, “inverse”, define como o Hibernate vai tratar a inserção e retirada de
objetos nessa associação. Quando um lado da associação define o atributo
45
“inverse” para “true” ele está indicando que apenas quando um objeto for inserido
do “outro lado” da associação ele deve ser persistido no banco de dados.
Para entender melhor o atributo “inverse” pense em como está definido o
mapeamento da classe Professor. Lá ele está definido para “true”, significando
que apenas quando uma Turma adicionar um professor, o relacionamento vai ser
persistido no banco de dados. Em código:
Professor professor = new Professor();
Turma turma = new Turma();
turma.setProfessor(professor);
professor.getTurmas().add(turma);
Listagem 7: Exemplo de código mostrando a importância de utilizar o “inverse”
Se apenas o Professor adicionando a Turma a sua coleção de Turmas,
nada ia acontecer ao banco de dados. Isso parece não ter muito sentido, mas se
você prestar bem atenção, o Hibernate não tem como saber qual dos dois lados
foi atualizado, desse modo ele vai sempre atualizar os dois lados duas vezes,
uma para cada classe da relação, o que seria desnecessário. Usando “inverse”
você define de qual lado o Hibernate deve esperar a atualização e ele vai fazer a
atualização apenas uma vez. Lembre-se sempre de adicionar os objetos dos dois
lados, pois em Java os relacionamentos não são naturalmente bidirecionais.
Voltando a o nó <set>, percebemos que dentro dele ainda existem mais
dois outros nós, <key> e <one-tomany>. O nó <key> representa a coluna da
tabela relacionada (neste caso, Turma) que guarda a chave estrangeira para a
classe Professor, nós adicionamos o atributo “column” e o valor é o nome da
coluna, que neste caso é “Pessoa_Professor_id”. No outro nó, <one-to-many>,
nós definimos a classe a qual pertence essa coleção de objetos, que é Turma
(lembre-se sempre de usar o nome completo da classe, com o pacote).
Depois do relacionamento um-para-muitos (1:N) vejamos agora como
mapear um relacionamento muitos-para-muitos (N:N). Em um banco de dados
relacional, este tipo de associação faz uso de uma “tabela de relação”, que
guarda as chaves estrangeiras das duas tabelas associadas, como ocorre no
nosso exemplo, onde tivemos que criar uma tabela “Turma_has_Aluno”, para
46
poder mapear o relacionamento N:N entre as classes Turma e Aluno no banco de
dados.
Como você já sabe, este tipo de associação também é definida usando o
nó <set>, vejamos então o nosso exemplo:
<joined-subclass name="Aluno" extends="Pessoa">
<key column="Pessoa_id"/>
<property name="matricula"/>
<set name="turmas"
table="Turma_has_Aluno"
inverse="false">
<key column="Aluno_Pessoa_id"/>
<many-to-many class="Turma" column="Turma_id"/>
</set>
</joined-subclass>
Listagem 8: Exemplo de código mostrando a utilização de “set”
Os atributos do nó <set> neste mapeamento são parecidos com os que
nós vimos no mapeamento da classe Professor, mas eles contêm algumas
informações adicionais graças ao tipo da associação. Primeiro, temos um novo
atributo no nó <set>, o “table”. Como nós havíamos falado antes, para uma
associação N:N você precisa de uma “tabela de relacionamento” e o atributo
“table” guarda o nome desta tabela. Mais uma vez o atributo “inverse” foi marcado
como “true” indicando que apenas as ações feitas do “outro lado” da associação
vão ser persistidas no banco de dados.
Dentro do nó, nós encontramos mais dois nós, <key>, que vai conter, no
atributo “column”, o nome da coluna que guarda a chave estrangeira para a tabela
Aluno e o nó <many-to-many>, que contém a classe dos objetos da coleção, no
atributo “class” e o nome da coluna na “tabela de relacionamento” que referencia
a chave primária da tabela Turma, no atributo “column”.
Vamos agora terminar de mapear as nossas outras classes do nosso
modelo e tornar as associações bidirecionais, como manda o nosso modelo de
classes. Começando pela classe Turma (o arquivo “Turma.hbm.xml”):
47
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="Turma">
<id name="id">
<generator class="increment"/>
</id>
<property name="nome"/>
<many-to-one
name="professor"
class="Professor"
column="Professor_Pessoa_id"/>
<many-to-one
name="disciplina"
class="Disciplina"
column="Disciplina_id"/>
<set name="alunos"
table="Turma_has_Aluno">
<key column="Turma_id"/>
<many-to-many
class="Aluno"
column="Aluno_Pessoa_id"/>
</set>
</class>
<query name="buscarTurmasPeloNome">
<![CDATA[from Turma t where t.nome = :nome]]>
</query>
</hibernate-mapping>
Listagem 9: Turma.hbm.xml - Mapeamento da classe Turma
A maior parte do código já é conhecida, mas ainda existem algumas coisas
que precisam ser esclarecidas. Turma é o lado “um” de dois relacionamentos umpara-muitos, um com a classe Professor e outro com a classe Disciplina, para
tornar essa associação bidirecional nós vamos utilizar o nó <many-to-one>, que
modela o lado “um” de uma associação um-para-muitos (1:N). Neste nó, nós
inserimos os atributos “name”, que recebe o nome da propriedade, “class”, que
48
recebe o nome da classe da propriedade e “column”, que recebe o nome da
coluna nesta tabela que guarda a chave estrangeira para a outra tabela do
relacionamento.
O resto do mapeamento é idêntico aos outros mapeamentos que nós já
estudamos, a única diferença é o lado da associação que ele está modelando. Os
dois outros mapeamentos, de Disciplina e Curso, não trazem nenhuma novidade
para o nosso estudo, portanto esteja livre para estudar os arquivos junto com o
resto do material de apoio.
4.8 – Configuração do Hibernate 3
A engine do Hibernate pode ser configurada de três modos diferentes,
instanciando um objeto de configuração (org.hibernate.cfg.Configuration) e
inserindo as suas propriedades programaticamente, usando um arquivo
.properties com as suas configurações e indicando os arquivos de mapeamento
programaticamente ou usando um arquivo XML (o “hibernate.cfg.xml”) com as
propriedades de inicialização e os caminhos dos arquivos de mapeamento.
Vejamos como configurar o Hibernate para o projeto:
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property
name="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property
name="hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<property
name="hibernate.connection.url">
jdbc:mysql://localhost/hibernate?autoReconnect=true
</property>
<property
name="hibernate.connection.username">
root
</property>
Listagem 10: hibernate.cfg.xml – Configuração do HIbernate
49
<property
name="hibernate.connection.password">
</property>
<!-- Condiguração do c3p0 -->
<property name="hibernate.c3p0.max_size">10</property>
<property name="hibernate.c3p0.min_size">2</property>
<property name="hibernate.c3p0.timeout">5000</property>
<property name="hibernate.c3p0.max_statements">10</property>
<property
name="hibernate.c3p0.idle_test_period">3000</property>
<property name="hibernate.c3p0.acquire_increment">2</property>
<!-- Configurações de debug -->
<property
<property
<property
<property
<mapping
<mapping
<mapping
<mapping
<mapping
<mapping
name="show_sql">true</property>
name="use_outer_join">true</property>
name="hibernate.generate_statistics">true</property>
name="hibernate.use_sql_comments">true</property>
<mapping resource="Curso.hbm.xml"/>
resource="Disciplina.hbm.xml"/>
resource="Turma.hbm.xml"/>
resource="Pessoa.hbm.xml"/>
resource="Aluno.hbm.xml"/>
resource="Professor.hbm.xml"/>
resource="Endereco.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Listagem 10: hibernate.cfg.xml – Configuração do Hibernate (continuação)
Na documentação do Hibernate você pode verificar todas as opções de
propriedades que podem ser utilizadas e seus respectivos resultados, por isso
nós vamos nos ater ao que é importante para começarmos a trabalhar. Vejamos
as propriedades:

hibernate.dialect: é a implementação do dialeto SQL específico do banco
de dados a ser utilizado.

hibernate.connection.driver_class: é o nome da classe do driver JDBC
do banco de dados que está sendo utilizado.

hibernate.connection.url: é a URL de conexão específica do banco que
está sendo utilizado.

hibernate.connection.username: é o nome de usuário com o qual o
Hibernate deve se conectar ao banco.
50

hibernate.connection.password: é a senha do usuário com o qual o
Hibernate deve se conectar ao banco.
Essa segunda parte do arquivo são as configurações do “pool” de
conexões escolhido para a nossa aplicação. No nosso exemplo o pool utilizado é
o C3P0, mas você poderia utilizar qualquer um dos pools que são oferecidos no
Hibernate ou então usar um DataSource do seu servidor de aplicação. Na terceira
parte, estão algumas opções para ajudar a debugar o comportamento do
Hibernate, a propriedade “show_sql” faz com que todo o código SQL gerado seja
escrito na saída default, “hibernate.generate_statistics” faz com que o Hibernate
gere estatísticas de uso e possa diagnosticar uma má performance do sistema e
“hibernate.use_sql_comments” adiciona comentários ao código SQL gerado,
facilitando o entendimento das queries.
A última parte do arquivo é onde nós indicamos os arquivos de
mapeamento que o Hibernate deve processar antes de começar a trabalhar, se
você esquecer de indicar um arquivo de mapeamento de qualquer classe, essa
classe não vai poder ser persistida pela engine do Hibernate. Outro detalhe
importante, é que quando você usa mapeamentos com Herança, o mapeamento
pai deve sempre vir antes do filho.
CAPÍTULO 5 – FUNCIONAMENTO DO HIBERNATE
Após ter realizado a configuração do Hibernate, ele já está pronto para
funcionar, tem-se de entender o mecanismo de persistência dele. Para o
Hibernate, existem três tipos de objetos, objetos “transient” (transientes),
“detached” (desligados) e “persistent” (persistentes). Objetos “transient” são
aqueles que ainda não tem uma representação no banco de dados (ou que foram
excluídos), eles ainda não estão sobre o controle do framework e podem não ser
mais referenciáveis a qualquer momento, como qualquer objeto normal em Java.
Objetos “detached” têm uma representação no banco de dados, mas não eles
fazem mais parte de uma sessão do Hibernate, o que significa que o seu estado
pode não estar mais sincronizado com o banco de dados. Objetos “persistent” são
os objetos que tem uma representação no banco de dados e que ainda fazem
parte de uma transação do Hibernate, garantindo assim que o seu estado esteja
sincronizado com o banco de dados (nem sempre, claro).
No Hibernate, assim como no JDBC, existem os conceitos de sessão e
transação. Uma sessão é uma conexão aberta com o banco de dados, onde nós
podemos executar queries, inserir, atualizar e deletar objetos, já a transação é a
demarcação das ações, uma transação faz o controle do que acontece e pode
fazer um roolback, assim como uma transação do JDBC, no caso de terem sido
encontrados problemas.
52
Edite o arquivo de configuração do Hibernate (hibernate.cfg.xml) com as
suas informações específicas (nome de usuário, senha, URL de conexão, etc),
coloque ele na raiz do seu classpath, junto com as classes compiladas e os
arquivos de mapeamento, porque nós vamos colocar o Hibernate pra funcionar
(tenha certeza de que o seu classpath está configurado corretamente, com todos
os arquivos necessários).
Primeiro, vamos criar uma classe para configurar e abrir as sessões do
Hibernate, o código é simples:
/*
* Criado em 04/05/2005
*
* Código desenvolvido por Francisco Augusto Franco do Nascimento
*
*/
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
/**
* @author Francisco
*
* Código desenvolvido por Francisco Augusto Franco do Nascimento
*
*/
public class HibernateUtility {
private static SessionFactory factory;
static {
//Bloco estático que inicializa o Hibernate
try {
factory = new Configuration().configure().buildSessionFactory();
} catch (Exception e) {
e.printStackTrace();
factory = null;
}
}
public static Session getSession() {
//Retorna a sessão aberta
return factory.openSession();
}
}
Listagem 11: HibernateUtility.java – Classe para configurar e abrir sessões do Hibernate
53
O bloco estático (as chaves marcadas como “static”) instancia um objeto de
configuração do Hibernate (org.hibernate.cfg.Configuration), chama o método
configure() (que lê o nosso arquivo hibernate.cfg.xml) e depois que ele está
configurado, criamos uma SessionFactory, que é a classe que vai ficar
responsável por abrir as sessões de trabalho do Hibernate. Faça um pequeno
teste pra ter certeza de que tudo está funcionando:
//Arquivo Teste1.java
public class Teste1 {
public static void main(String[] args) {
Session sessao = HibernateUtility.getSession(); //Abrindo
uma sessão
Transaction transaction = sessao.beginTransaction();
//Iniciando uma transação
Curso curso = new Curso(); //Instanciando um objeto
transiente
curso.setNome("Desenvolvimento de Software"); //Preenchendo
as propriedades do objeto
curso.setDescricao("Curso só pra programadores");
sessao.save(curso);
//Transformando o objeto transiente
em um objeto
//persistente no banco de dados
transaction.commit(); //Finalizando a transação
sessao.close(); //Fechando a sessão
}
}
Listagem 12: Teste1.java – Teste de utilização do Hibernate para persistência de uma
instância da classe Curso
Se ele não lançou nenhuma exceção, o seu ambiente está configurado
corretamente. Vamos entender agora o que foi que aconteceu neste código,
primeiro nós inicializamos a SessionFactory, dentro do bloco estático na classe
HibernateUtility, depois que ela é inicializada, o método getSession() retorna uma
nova sessão para o código dentro do main(). Após a sessão ter sido retornada,
nós iniciamos uma transação, instanciamos um objeto Curso, preenchemos as
suas propriedades e chamamos o método save() na sessão. Após o método
54
save(), finalizamos a transação e fechamos a sessão. Acabamos de inserir um
registro no banco de dados sem escrever nenhuma linha de SQL, apenas com a
chamada de um método.
O código de aplicações que usam o Hibernate costuma mostrar esse
mesmo comportamento, abrir sessão, iniciar transação, chamar os métodos
save(), update(), get(), delete(), etc, fechar a transação e depois a sessão, um
comportamento muito parecido com o de aplicações JDBC comuns, a diferença é
que não escrevemos nem uma linha sequer de SQL para tanto.
5.1 – Realização de Pesquisas no Banco usando o Hibernate
Agora que você já sabe mapear e configurar a fera, podemos passar para
uma das partes mais importantes do funcionamento do framework, as pesquisas.
Existem três meios de se fazer buscas usando o Hibernate, usando a sua
linguagem própria de buscas, a Hibernate Query Language (HQL), usando a sua
Criteria Query API (para montar buscas programaticamente) e usando SQL puro.
A maioria das suas necessidades deve ser suprida com as duas primeiras
alternativas, o resto, você sempre pode usar SQL pra resolver.
A HQL é uma extensão da SQL com alguns adendos de orientação a
objetos, nela você não vai referenciar tabelas, vai referenciar os seus objetos do
modelo que foram mapeados para as tabelas do banco de dados. Além disso, por
fazer pesquisas em objetos, você não precisa selecionar as “colunas” do banco
de dados, um select assim: “select * from Turma” em HQL seria simplesmente
“from Turma”, porque não estamos mais pensando em tabelas e sim em objetos.
A Criteria Query API é um conjunto de classes para a montagem de
queries em código Java, você define todas as propriedades da pesquisa
chamando os métodos e avaliações das classes relacionadas. Como tudo é
definido programaticamente, você ganha todas as funcionalidades inerentes da
programação orientada a objetos para montarem as suas pesquisas e ainda
garante a completa independência dos bancos de dados, pois a classe de “dialeto
SQL” do seu banco vai se encarregar de traduzir tudo o que você utilizar.
55
Antes de ver os exemplos propriamente ditos, vejamos como criar um
objeto que implemente a interface Query ou Criteria. Para instanciar um desses
objetos, você vai ter que abrir uma sessão do Hibernate, como nós já vimos na
listagem do arquivo “Teste1.java”. Vejamos como ficaria o código:
//Arquivo Teste2.java
public class Teste2 {
public static void main(String[] args) {
Session sessao = HibernateUtility.getSession(); //Abrindo uma
sessão
Transaction tx = sessao.beginTransaction(); //Iniciando uma
transação
Query select = sessao.createQuery("from Turma");
List objetos = select.list();
System.out.println(objetos); //Transformando o objeto
transiente em um objeto
//persistente no banco de dados
tx.commit(); //Finalizando a transação
sessao.close(); //Fechando a sessão
}
}
Listagem 13: Teste2.java – Teste para consultar dados de uma Turma usando HQL
O código ainda segue aquela mesma seqüência da listagem anterior, abrir
uma sessão, iniciar uma transação e começar a acessar o banco de dados. Para
criar uma query, nós chamamos o método createQuery(String query) na sessão,
passando como parâmetro o String que representa a query. Depois disso,
podemos chamar o método list(), que retorna um “List” com os objetos resultantes
da query. Usando a Criteria API, o código ficaria desse modo:
56
//Arquivo Teste3.java
public class Teste3 {
public static void main(String[] args) {
Session sessao = HibernateUtility.getSession(); //Abrindo uma
sessão
Transaction tx = sessao.beginTransaction(); //Iniciando uma
transação
Criteria select = sessao.createCriteria(Turma.class);
List objetos = select.list();
System.out.println(objetos); //Transformando o objeto
transiente em um objeto
//persistente no banco de dados
tx.commit(); //Finalizando a transação
sessao.close(); //Fechando a sessão
}
}
Listagem 14: Teste3.java – Teste para consultar dados de uma Turma usando Criteria API
Veja que apenas a linha onde nós criávamos a query mudou. Agora, em
vez de chamar o método createQuery(String query), nós chamamos o método
createCriteria(Class clazz), passando como parâmetro a classe que vai ser
pesquisada, que no nosso caso é Tuma.
Outro complemento importante do Hibernate é o suporte a paginação de
resultados. Para paginar os resultados, nós chamamos os métodos
setFirstResult(int first) e setMaxResults(int max). O primeiro método indica de qual
posição os objetos devem começar a serem carregados e o segundo indica o
máximo de objetos que devem ser carregados. Estes métodos estão presentes
tanto na interface “Query” quanto na interface “Criteria”. Vejamos como nós
deveríamos proceder para carregar apenas as dez primeiras turmas do nosso
banco de dados:
Session sessao = HibernateUtility.getSession();
Transaction tx = sessao.beginTransaction();
Criteria select = sessao.createCriteria(Turma.class);
select.setFirstResult(0);
select.setMaxResults(10);
List objetos = select.list();
System.out.println(objetos);
tx.commit();
sessao.close();
Listagem 15: Trecho para consultar as 10 primeiras turmas
57
Veja que nós chamamos os métodos setFirstResult() e setMaxResults()
antes de listar os objetos da query e lembre-se também que a contagem de
resultados (assim como os arrays) começa em zero, não em um.
Uma propriedade específica da HQL (que foi herdada do JDBC), é o uso
de parâmetros nas queries. Assim como no JDBC, os parâmetros podem ser
numerados, mas também podem ser nomeados, o que se simplifica ainda mais o
uso e a manutenção das queries, porque a troca de posição não vai afetar o
código que as usa. Vejamos um exemplo do uso de parâmetros:
Session sessao = HibernateUtility.getSession();
Transaction tx = sessao.beginTransaction();
Query select = sessao.createQuery("from Turma as turma where turma.nome
= :nome");
select.setString("nome", "Jornalismo");
List objetos = select.list();
System.out.println(objetos);
tx.commit();
sessao.close();
Listagem 16: Trecho para consultar turmas usando parametro nomeado
Nesse nosso novo exemplo, tivermos mais algumas adições a query HQL.
A primeira é o uso do “as”, que serve para “apelidar” uma classe no na nossa
expressão (do mesmo modo do “as” em SQL), ele não é obrigatório, o código
poderia estar “from Turma turma ...” e ele funcionaria normalmente. Outra coisa a
se notar, é o acesso as propriedades usando o operador “.” (ponto), você sempre
acessa as propriedades usando esse operador. E a última parte é o parâmetro
propriamente dito, que deve ser iniciado com “:” (dois pontos) para que o
Hibernate saiba que isso é um parâmetro que vai ser inserido na query. Insere-se
um parâmetro nomeado usando o método “set” correspondente ao tipo de
parâmetro, passando primeiro o nome com o qual ele foi inserido e depois o valor
que deve ser colocado.
E se você também não gosta de ficar metendo código SQL no meio do seu
programa,
porque deveria
meter
código
HQL?
O
Hibernate facilita
a
externalização de queries HQL (e até SQL) com os nós <query> e <sql-query>
58
nos arquivos de mapeamento. Vamos adicionar uma query no mapeamento da
classe turma (o arquivo “Turma.hbm.xml”):
<query name="buscarTurmasPeloNome">
<![CDATA[from Turma t where t.nome = :nome]]>
</query>
Listagem 17: Trecho mostrando a criação de consultas externas nomeadas com “query”
Essas linhas adicionadas no arquivo de mapeamento vão instruir o
Hibernate a criar um PreparedStatement para esse select, fazendo com que ele
execute mais rápido e possa ser chamado de qualquer lugar do sistema. Você
deve preencher o atributo “name” com o nome pelo qual esta query deve ser
chamada na aplicação e no corpo do nó você deve colocar o código da query, de
preferência dentro de uma tag CDATA, pra evitar que o parser XML entenda o
que está escrito na query como informações para ele. Veja como executar uma
“named query”:
Session sessao = HibernateUtility.getSession();
Transaction tx = sessao.beginTransaction();
Query select = sessao.getNamedQuery("buscarTurmasPeloNome");
select.setString("nome", "Jornalismo");
List objetos = select.list();
System.out.println(objetos);
tx.commit();
sessao.close();
Listagem 18: Trecho mostrando a utilização de consultas externas nomeadas
A única diferença para as outras listagens é que em vez de escrevermos a
query dentro do código Java, ela ficou externalizada no arquivo de mapeamento.
Para instanciar o objeto, chamamos o método getNamedQuery(String name),
passando como parâmetro o nome da query definido no atributo “name” no
arquivo XML.
59
5.2 – Restrições as Pesquisas
Nesta seção, iremos entender como adicionar restrições as nossas
pesquisas no Hibernate. Os exemplos vão seguir uma dinâmica simples, com
uma versão em HQL e como o mesmo exemplo poderia ser feito usando a API de
Criteria.
A HQL e a API de Criteria suportam todos os operadores de comparação
da SQL (<, >, <>, <=, >=, =,between, not between, in e not in), vejamos um
exemplo em HQL:
from Aluno al where al.endereco.numero >= 50
O uso dos operadores de comparação segue a mesma sintaxe da
linguagem SQL, usando Criteria, essa mesma query poderia ser expressa da
seguinte maneira:
Criteria select = sessao.createCriteria(Aluno.class);
select.createCriteria("endereco").add( Restrictions.get( "numero", new
Integer(10) ) );
Nós criamos a o objeto Criteria usando a classe Aluno, mas a nossa
restrição é para a propriedade “numero” da classe Endereco que está associada a
classe Aluno, então temos que “descer” um nível, para poder aplicar a restrição a
propriedade “numero” de “endereco”. Usando Criteria, você pode usar os métodos
estáticos da classe “org.hibernate.criterion.Restrictions” para montar expressões.
Os operadores lógicos (e os parênteses) também estão a sua disposição e
em HQL eles continuam com a mesma sintaxe do SQL. Você pode usar or, and e
parênteses para agrupar as suas expressões. Vejamos um exemplo:
from Endereco end where ( end.rua in (“Epitácio Pessoa”, “Ipiranga”) ) or
( end.numero between 1 and 100 )
Passando para Criteria:
Criteria select = sessao.createCriteria(Endereco.class);
String[] ruas = {"Epitácio Pessoa", "Ipiranga"};
select.add(
Restrictions.or(
Restrictions.between("numero", new Integer(1), new
Integer(100) ),
60
Restrictions.in( "rua", ruas )
)
);
O código usando Criteria é muito mais longo do que a expressão em HQL
e também é menos legível. Procure utilizar Criteria apenas quando for uma busca
que está sendo montada em tempo de execução, se você pode prever a busca,
use “named queries” e parâmetros para deixar o eu código mais limpo e mais
simples.
Outra parte importante das pesquisas são as funções de pesquisa em
Strings. Você pode usar “LIKE” e os símbolos “%” e “_”, do mesmo modo que eles
são utilizados em SQL. Vejamos um exemplo:
from Curso c where c.nome like “Desenvolvimento%”
Em Criteria:
Criteria select = sessao.createCriteria(Curso.class);
select.add( Restrictions.like("nome", "Desenvolvimento", MatchMode.START)
);
Além disso, pode-se ainda ordenar as suas pesquisas, em ordem
ascendente ou descendente. Como nos exemplos a seguir:
from Aluno al order by al.nome asc
Em Criteria:
Criteria select = sessao.createCriteria(Aluno.class);
select.addOrder( Order.asc("nome") );
CAPÍTULO 6 – CRIANDO UMA APLICAÇÃO COMPLETA
USANDO O HIBERNATE
Agora que já analisamos individualmente cada recurso do Hibernate vamos
partir para a criação de uma aplicação simples, que vai demonstração toda a
facilidade de utilização da ferramenta.
Vamos necessitar do ambiente Eclipse para desenvolver esta aplicação,
por isto é bom já estar familiarizado com ele e enterder como o utilizá-lo. Bem,
pode ser utilizada qualquer outra ferramenta, basta converter os procedimentos
específicos do Eclipse para ela.
O primeiro passo é a criação de Java Project (Projeto Java). Para tal siga
os seguintes passos:
1. Escolha “File” / “New” / “Project”;
2. Vai abrir um Wizard chamado “New Project”, nele voçê irá selecionar
“Java Project” e depois clique em “Next >”;
3. O nome da nossa aplicação será Universidade, então em Project
Name, digite “Universidade”. Clique em “Create new project in
workspace” e em seguida em “Next >”;
4. Após isto, aparecerá a tela “Java Settings”, nela iremos personalizar
as opções do projeto java. O importante no momento é incluir no
projeto os pacotes da biblioteca do Hibernate e da biblioteca do
62
Connector/J para acesso ao MySQL. Selecione “Libraries” / “Add
External JARs”;
5. Para adicionar o suporte ao Hibernate e assumindo que foi ele foi
instalado
na
pasta
“C:\Hibernate-3.0”,
adicione
o
arquivo
“hibernate3.jar” que está na raiz da pasta. Selecione “Libraries” /
“Add External JARs” novamente e incluia todos os arquivos “.jar” da
pasta “lib” do Hibernate;
6. Para adicionar o suporte ao Connector/J e assumindo que foi ele foi
instalado na pasta “C:\JDBC”, adicione o arquivo “mysql-connectorjava-3.1.7-bin.jar” que está na raiz da pasta;
7. Selecione “Finish” e pronto! Agora podemos começar o nosso
projeto;
Já que temos um novo projeto no Eclipse, temos que colocar na mesma
pasta do projeto todos os arquivos de mapeamento XML das classes. Para tal,
primeiro temos que ter certeza onde está à pasta criada para o projeto (criada na
workspace atual). Clique com o botão direito no “Package Explorer” em
“Universidade”, depois em “Properties”.
Na tela que aparece clique em “Info” e encontre o campo “Location”. Ele diz
qual é o caminho da pasta onde está o projeto Java e onde ficarão os códigosfontes em java.
Como nós já criamos alguns arquivos XML, recomendamos que eles sejam
copiados para esta pasta. Então, copie para ela os arquivos Pessoa.hbm.xml, ,
Endereco.hbm.xml, Professor.hbm.xml, Aluno.hbm.xml, Turma.hbm.xml.
Para completar o conjunto de classes de domínio apresentamos a seguir
as listagens dos arquivos de mapeamento das classes Curso (Curso.hbm.xml) e
Disciplina (Disciplina.hbm.xml).
63
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="Curso">
<id name="id">
<generator class="increment"/>
</id>
<property name="nome"/>
<property name="descricao"/>
<set name="disciplinas"
inverse="true"
cascade="save-update">
<key column="Curso_id"/>
<one-to-many class="Disciplina"/>
</set>
</class>
</hibernate-mapping>
Listagem 19: Curso.hbm.xml - Mapeamento da classe Curso
64
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="Disciplina">
<id name="id">
<generator class="increment"/>
</id>
<property name="ementa"/>
<property name="nome"/>
<set name="turmas"
inverse="true">
<key column="Disciplina_id"/>
<one-to-many class="Turma"/>
</set>
<many-to-one
name="curso"
class="Curso"
column="Curso_id"/>
</class>
</hibernate-mapping>
Listagem 20: Disciplina.hbm.xml - Mapeamento da classe Disciplina
Já com os arquivos de mapeamento prontos, precisamos criar as classes
em Java que irão ser mapeadas. Neste caso utilizamos para fazer isto o Jude,
uma ferramenta muito útil que é capaz de criar 9 dos 13 diagramas da UML, além
de fácil utilização e possuir uma versão gratuita. Para criá-los siga os seguintes
passos:

Inicie o Jude. Clique “Diagram / Class Diagram”;

Aparecerá uma tela em branco com uma barra de ferramentas em
cima. Crie uma diagrama de classe como na figura 2 (mostrada
anteriormente).

Clique em “Tools / Export Java” na barra de menu.
65

No quadro de diálogo encontra a pasta do projeto e clique em
“Open”;

O Jude irá criar os arquivos .java das classes de domínio nesta
pasta;
Normalmente para as classes do domínio não se criam os métodos de
acesso nos modelo. Para isto o Eclipse poder criá-los todos, automaticamente,
para isto abra cada arquivo .java e clicando com o botão direito selecione em
“Source / Generation Getter and Setter method”.
Alternativamente poder-se criar cada arquivo individualmente através de
qualquer outro método. A listagem dos arquivos encontra-se no Apêndice A.
Para
conficurar
o
Hibernate,
copie
para
esta
pasta
o
arquivo
hibernate.cfg.xml criado anteriormente.
Além das classes de domínio descritas na figura 2, nós vamos precisar da
classe HibernateUtility que criamos anteriormente, e mais algumas classes de
controle para demonstrar as funcionalidades básicas do Hibernate. A primeira a
ser criada será chamada de AplicacaoUniversidade, e irá ser o ponto de entrada
(o “main()”).
Crie uma classe chamada AplicacaoUniversidade. Faça isto através dos
seguintes passos:
1. Escolha “File” / “New” / “Class”;
2. Vai abrir um Wizard chamado “New Java Class”. O nome da nossa
nova classe será AplicacaoUniversidade, então em Name, digite
“AplicacaoUniversidade”. Na opção “Which method stubs would you
like to create?”, deixe apenas a opção “public static void
main(String[] args)” marcada e em seguida em “Next >”;
O código fonte para a classe AplicacaoUniversidade é:
66
public class AplicacaoUniversidade {
/**
* @param args
*/
public static void main(String[] args) {
String saida = null;
Leitor leitor = new Leitor(System.in);
do {
System.out.println("============================" +
"==============");
System.out.println("=== Universidade - Tutorial
Hibernate ===");
Listagem 21: AplicacaoUniversidade.java – Ponto de entrada
67
System.out.println("==========================================");
System.out.println("1 - Listar todos os cursos");
System.out.println("2 - Listar todas as disciplinas");
System.out.println("3 - Listar todos os cursos e suas
disciplinas");
System.out.println("4 - Cadastrar novo curso");
System.out.println("5 - Cadastrar nova disciplina");
System.out.println("6 - Remove disciplina");
System.out.println("7 - Altera o nome do curso");
System.out.println("8 - Cadastrar novo aluno");
System.out.println("9 - Listar todos os alunos");
System.out.println("S - Sair");
saida = leitor.leDoTeclado("Digite a sua opção:");
if (saida.equalsIgnoreCase("s"))
break;
try {
int opcao = Integer.parseInt(saida);
switch (opcao) {
case 1:
new ListaCursos(false);
break;
case 4:
new NovoCurso();
break;
default:
throw new NumberFormatException();
}
} catch (Exception e) {
System.out.println("Opção inválida!");
}
} while (true);
System.out.println("==================================" +
"=========");
System.out.println("=== Universidade - Tutorial " +
"Hibernate ====");
System.out.println("==================================" +
"=========");
System.out.println("(C) 2005 - Criado por Francisco
Augusto");
}
}
Listagem 21: AplicacaoUniversidade.java – Ponto de entrada (continuação)
6.1 – Descrevendo o código da classe AplicacaoUniversidade
Poderíamos fazer utilizando o pacote Swing, com ele iríamos fizer com
uma interface com o usuário bem melhor, e muito mais intuitiva, porém iríamos
adicionar uma complexidade que não é conveniente neste momento.
68
Na listagem 22 podemos visualizar a classe AplicacaoUniversidade. Nesta
classe temos o ponto de entrada da nossa aplicação de demonstração. Ele está
utilizando o console, já que
se fossemos utilizar os recurso de GUI, iríamos
adicionar muita complexidade desnecessária. O importante é entender como o
Hibernate funciona e como utilizá-lo com eficiência.
Na linha 6, declaramos o método main e iniciamos a aplicação
propriamente dita. Definimos uma variável local chamada saída, ela irá contém a
opção selecionada pelo usuário através do menu. Criamos uma instância da
classe Leitor passando como parâmetro do construtor a entrada padrão do
console (System.in) , ela é responsável por permitir que o usuário utilize o console
para entrada de dados.
Em seguida, iniciaremos um loop e mostraremos as opções do menu da
tela do console (System.out). Aguardamos o usuário escolher uma opção e
verificamos se é a opção de saida da aplicação, se for ela sai.
Para simplificar, faremos uma conversão de String para int, assim
poderemos realizar um parsing das opções com uma única instrução switch. Para
cada caso, crie uma nova instância da classe de Controle. A partir deste popnto o
controle será passado para está classe de formal Modal, ou seja, até que a
instância termine sua execução e o controle retornará para o método main.
Para finalizar, terminamos o loop. Foi utilizada uma estratégia de loop
infinito e com saida através de break, aqui poderíamos testar também a opção e
sair. Após, uma mensagem de finalização e encerramos a execução da aplicação.
Do mesmo modo que esta classe foi criada, cria-se uma nova classe
chamada Leitor. Esta classe tem a finalidade de permitir que o usuário utilize o
console para fazer a entrada de dados. Ela é utilizada em toda a nossa pequena
aplicação. O código fonte para a classe Leitor é:
69
import
import
import
import
java.io.BufferedReader;
java.io.IOException;
java.io.InputStream;
java.io.InputStreamReader;
public class Leitor {
private BufferedReader buffer;
public Leitor(InputStream inputStream) {
buffer = new BufferedReader(new
InputStreamReader(inputStream));
}
/**
* @param Descrição
* @return Entrada do teclado
*/
public String leDoTeclado(String descricao) {
String texto = null;
try {
System.out.print(descricao + "> ");
texto = buffer.readLine();
} catch (IOException e) {
}
return texto;
}
}
Listagem 22: Leitor.java – Classe utilitária para entrada de dados pelo console
6.2 – Descrição da classe Leitor
Na listagem 23 podemos visualizar a classe LeitorHibernateUtility. Nessa
classe foi criada para permitir a entrada de dados pelo console.
Ela contém
apenas um método, o leDoTeclado(), que realiza esta operação.
Seu funcionamento é bem simples, quando a classe é criada, no seu
construtor é passado um InputStream, no nosso caso utilizamos o objeto
System.in, que será a fonte dos dados de entrada.
Para sua utilização, envia-se uma mensagem através do método
leDoTeclado, passando uma mensagem. Este mostra a mensagem na saida
padrão, fica esperado uma entrada do console no estado Modal, quando o
usuário tecla Enter, ele retorna uma String contendo o que o usuário digitou.
Cria-se uma nova classe chamada ListarCursos. O código fonte para ela é:
70
import java.util.Iterator;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.HibernateException;
public class ListaCursos {
/**
* @param mostraDisciplinas
*/
public ListaCursos(boolean mostraDisciplinas) {
try {
Session session = HibernateUtility.getSession();
Query query = session.createQuery("from Curso a");
System.out.println("========================================");
System.out.println("============= Resultados
=============");
System.out.println("========================================");
for (Iterator it = query.iterate(); it.hasNext();) {
// exibe os cursos
Curso curso = (Curso) it.next();
System.out.println("Curso: " + curso.getId() +
"-"
+ curso.getNome() + " - " +
curso.getDescricao());
// exibe as disciplinas dos cursos
if (mostraDisciplinas)
for (Iterator it2 =
curso.getDisciplinas().iterator(); it2
.hasNext();) {
Disciplina disciplina = (Disciplina)
it2.next();
System.out.println("Disciplina:" +
disciplina.getId()
+ "-" +
disciplina.getNome() + " - "
+
disciplina.getEmenta());
}
}
session.close();
} catch (HibernateException e) {
e.printStackTrace();
}
6.3 – Descrevendo o código da classe ListarCurso
}
}
Listagem 23: ListarCursos.java – Listagem de Cursos
Esta classe tem o objetivo de listar os cursos e suas disciplinas associadas.
Ela irá criar as instâncias e mostrará utilizando a saída padrão.
71
Observando o código-fonte da listagem 25 vemos o procedimento para
configuração do Hibernate, através da classe HibernateUtility, é realizado na linha
13.
Ele cria uma intancia da classe Session do próprio Hibernate. A verificação
dos arquivos XML é feita na linha 13. Se nenhuma exceção for gerada no parser
do arquivo XML, obtemos uma instância de Session para a aplicação. A partir
desse momento, estamos prontos para realizar a consulta.
Para a criação da consulta utilizamos um objeto da classe Query. Ela é que
fisicamente pega os dados do banco de dados e cria os objetos, retornando-os de
forma a poderem ser utilizados com um Iterator. Fato que acontece na linha 18.
A partir deste ponto, pode-se trabalhar da mesma maneira em que faz para
utilizar um Set ou um List. A maior diferença é que no final, deve-se fechar o
objeto Session, através do método close(). E de resto só uma verificação de
exceção para a classe HibernateException.
Agora, cria-se uma nova classe chamada NovoCurso.
import org.hibernate.Session;
import org.hibernate.HibernateException;
import org.hibernate.Transaction;
public class NovoCurso {
public NovoCurso() {
Listagem 24: NovoCursos.java – Inclusão de Novos Cursos
72
Leitor leitor = new Leitor(System.in);
try {
Transaction tx = null;
Session session = HibernateUtility.getSession();
String sid;
// determina o inicio da transacao
tx = session.beginTransaction();
Curso curso = new Curso();
// le os dados
sid = leitor.leDoTeclado("Digite o número do curso:");
try {
Integer iid = new
Integer(Integer.parseInt(sid));
curso.setId(iid);
} catch (Exception e) {
System.out.println("Número inválido!");
}
curso.setNome(leitor.leDoTeclado("Digite o nome do
curso:"));
curso.setDescricao(leitor
.leDoTeclado("Digite o descrição do
curso:"));
// grava os dados
session.save(curso);
// confirma as alteracoes da transacao no banco
(commit)
tx.commit();
session.close();
} catch (HibernateException e) {
e.printStackTrace();
}
}
}
Listagem 24: NovoCursos.java – Inclusão de Novos Cursos (continuação)
6.4 – Execução da aplicação
Antes de executarmos a aplicação, devemos configurar o arquivo
hibernate.properties, que armazena as informações de conexão com o banco de
dados.
Analisamos as linhas 12 a 17 da listagem 10, vemos que as configurações
com o MySQL, encontram-se ativas.
O arquivo hibernate.cfg.xml deve estar no diretório-raiz da aplicação, para
que o Hibernate funcione de maneira adequada.
Finalmente estamos prontos. Dentro do Eclipse digite F5
73
Execute a aplicação. Faça o seguinte:

Crie um novo Curso chamado “Tecnólogo em Informática”.

Escolha a opção de listar cursos.

Agora , escolha a opção de listar curso e disciplinas.
Perceba que o resultado é o mesmo, pois não existem disciplinas a serem
listadas. Agora, crie uma classe chamada NovaDisciplina.
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import
import
import
import
org.hibernate.Query;
org.hibernate.Session;
org.hibernate.HibernateException;
org.hibernate.Transaction;
public class NovaDisciplina {
public NovaDisciplina() {
Leitor leitor = new Leitor(System.in);
try {
Transaction tx = null;
Session session = HibernateUtility.getSession();
String sid;
// determina o inicio da transacao
tx = session.beginTransaction();
Disciplina disciplina = new Disciplina();
Map lista = new HashMap();
// le os dados
sid = leitor.leDoTeclado("Digite o número da
disciplina:");
try {
Integer iid = new
Integer(Integer.parseInt(sid));
disciplina.setId(iid);
} catch (Exception e) {
System.out.println("Número inválido!");
}
disciplina.setNome(leitor.leDoTeclado("Digite o
nome:"));
disciplina.setEmenta(leitor
.leDoTeclado("Digite o descrição do
curso:"));
Listagem 25: NovaDisciplina.java – Inclusão de Novas Disciplinas
74
// lista artigos existentes
Query query = session.createQuery("from Curso");
for (Iterator it = query.iterate(); it.hasNext();) {
Curso curso = (Curso) it.next();
System.out.println("> " + curso.getId() + "-" +
curso.getNome()
+ " - " + curso.getDescricao());
lista.put(curso.getId().toString(), curso);
}
int codigoCurso = Integer.parseInt(leitor
.leDoTeclado("Digite o numero do
artigo:"));
disciplina.setCurso((Curso)
lista.get(String.valueOf(codigoCurso)));
// grava os dados
session.save(disciplina);
// confirma as alteracoes da transacao no banco
(commit)
tx.commit();
session.close();
} catch (HibernateException e) {
e.printStackTrace();
}
}
Listagem 25: NovaDisciplina.java – Inclusão de Novas Disciplinas (continuação)
Feito isto, altere a classe AplicacaoUniversidade, acrescentando o seguinte
código em destaque:
case 3:
new ListaCursos(true);
break;
case 4:
new NovoCurso();
break;
case 5:
new NovaDisciplina();
break;
default:
throw new NumberFormatException();
Listagem 26: AplicacaoUniversidade.java – Detalhe para chamada de Lista de Cursos e
Novas Disciplinas
Listagem 29: AplicacaoUniversidade.java – Detalhe para chamada de Inclusão de Novas
Disciplinas
6.5 – Recuperação das informações
Novamente, execute a aplicação e faça o seguinte:
75

Escolha a opção de listar curso e disciplinas e observe os
resultados.

Crie uma nova Disciplina chamada “Introdução à Ciência da
Computação” e associe-a ao curso “Tecnólogo em Informática”.

Agora , escolha a opção de listar curso e disciplinas novamente.
Perceba que o resultado não é mais o mesmo, pois já existem disciplinas a
serem listadas com os cursos. Quando se associa uma disciplina ao curso, e ela é
salva, quando o curso é recuperado novamente, a relação com a displina é
novamente reestabelecida, isto acontece porque o Hibernate conhece o modelo e
cria na memória todos os objetos filhos necessários para para cada objeto. Isto
significa que quando um objeto é criado, o modelo está sempre consistente.
Crie uma nova classe chamada RemoverDisciplinas. O código fonte para
ela é:
import java.util.Iterator;
import
import
import
import
org.hibernate.HibernateException;
org.hibernate.Query;
org.hibernate.Session;
org.hibernate.Transaction;
public class RemoverDisciplina {
public RemoverDisciplina() {
Leitor leitor = new Leitor(System.in);
try {
Transaction tx = null;
Session session = HibernateUtility.getSession();
// determina o inicio da transacao
tx = session.beginTransaction();
Disciplina[] lista = new Disciplina[20];
// lista os links existentes
Query query = session.createQuery("from Disciplina");
for (Iterator it = query.iterate(); it.hasNext();) {
Disciplina disciplina = (Disciplina) it.next();
System.out.println("> " + disciplina.getId() +
"-"
+ disciplina.getEmenta());
lista[Integer.parseInt(disciplina.getId().toString())] =
disciplina;
}
int codigoDisciplina = Integer.parseInt(leitor
.leDoTeclado("Digite o numero da
disciplina para remover:"));
Listagem 27: RemoverDisciplinas.java – Remoção de Disciplinas
76
// remove os dados
session.delete(lista[codigoArtigo]);
// confirma as alteracoes da transacao no banco
(commit)
tx.commit();
session.close();
} catch (HibernateException e) {
e.printStackTrace();
}
}
}
Listagem 27: RemoverDisciplinas.java – Remoção de Disciplinas (continuação)
Feito isto, altere a classe AplicacaoUniversidade, acrescentando o seguinte
código em destaque:
case 4:
new NovoCurso();
break;
case 5:
new NovaDisciplina();
break;
case 6:
new RemoveDisciplina();
break;
default:
throw new NumberFormatException();
Listagem 28: AplicacaoUniversidade.java – Detalhe para chamada de Remoção de
Disciplinas
Novamente, execute a aplicação e faça o seguinte:

Escolha a opção de listar curso e disciplinas e observe os
resultados.

Remova
a
Disciplina
chamada
“Introdução
à
Ciência
da
Computação.

Agora , escolha a opção de listar curso e disciplinas novamente.
Crie uma outra classe chamada AlteraNomeCurso. O código fonte para ela
é:
77
import java.util.Iterator;
import
import
import
import
org.hibernate.Query;
org.hibernate.Session;
org.hibernate.HibernateException;
org.hibernate.Transaction;
public class AlteraNomeCurso {
public AlteraNomeCurso() {
Leitor leitor = new Leitor(System.in);
try {
Transaction tx = null;
Session session = HibernateUtility.getSession();
// determina o inicio da transacao
tx = session.beginTransaction();
Curso[] lista = new Curso[20];
// lista artigos existentes
Query query = session.createQuery("from Curso");
for (Iterator it = query.iterate(); it.hasNext();) {
Curso curso = (Curso) it.next();
System.out.println("> " + curso.getId() + "-" +
curso.getNome()
+ " - por:" + curso.getDescricao());
lista[Integer.parseInt(curso.getId().toString())] = curso;
}
int codigoCurso = Integer.parseInt(leitor
.leDoTeclado("Digite o numero do
curso:"));
// altera os dados
Curso curso = lista[codigoCurso];
curso.setNome(leitor.leDoTeclado("novo nome"));
// grava os dados
session.update(curso);
Listagem 29: AlteraNomeCurso.java – Alteração de Nome do Curso
// confirma as alteracoes da transacao no banco
(commit)
tx.commit();
session.close();
} catch (HibernateException e) {
e.printStackTrace();
}
}
}
Listagem 29: AlteraNomeCurso.java – Alteração de Nome do Curso (continuação)
78
Feito isto, altere a classe AplicacaoUniversidade, acrescentando o seguinte
código em destaque:
case 6:
new RemoveDisciplina();
break;
case 7:
new AlteraNomeCurso();
break;
default:
throw new NumberFormatException();
Listagem 30: AplicacaoUniversidade.java – Detalhe para chamada de Alteração de Nome
do Curso
Execute a aplicação e faça o seguinte:

Escolha a opção de listar curso.

Altere o Eome do Curso de
“Tecnólogo em Informática” para
“Bacharel em Ciência da Computação”.

Escolha a opção de listar curso.
Para exercitar-se faça o seguinte:

Crie classes para criar, listar, remover e alterar alunos, professores e
turmas;

Em seguinda, crie uma rotina de matrícula de aluno na turma
6.6 – Conclusão
Utilizamos uma aplicação de demonstração que foi modificada para estar
mais próxima de um processo real de desenvolvimento. Criamos um aplicativo
Java, utilizando J2SDK 1.5, que realiza chamadas às classes e interfaces
fornecidas pelo Hibernate.
Por motivo de simplificação, não foi demonstrado todo o potencial do
Hibernate com o gerenciamento de transações ou a possibilidade de um EJB
obter uma fábrica de sessões (SessionFactory), utilizando Java Naming and
Directory Interface (JNDI) Lookup.
CAPÍTULO 7 – CONCLUSÃO
O Hibernate é um framework muito bom, e que facilita muito o
desenvolvimento de aplicações que acessam bancos de dados, fazendo com que
o programador se preocupe mais com o seu modelo de objeto e seus
comportamentos, do que com as tabelas do banco de dados. O Hibernate
também evita o trabalho de escrever dúzias de código repetido para fazer as
mesmas coisas, como “inserts”, “selects”, “updates” e “deletes” no banco de
dados, além de ter duas opções para se montar buscas complexas em grafos de
objetos e ainda uma saída para o caso de nada funcionar, usar SQL.
Demonstramos através de um exemplo, como realizar o mapeamento OO x
SGBDR através dele, um projeto OpenSource e Free. Considerando ainda que
estamos utilizando classes Java, as chamadas ao Hibernate poderiam ser
realizadas através de Java Server Pages (JSP), Servets ou Enterprise Java
Beans (EJB).
Mas é importante salientar que o framework não é uma boa opção para
todos os tipos de aplicações. Sistemas que fazem uso extensivo de stored
procedures, triggers ou que implementam a maior parte da lógica da aplicação no
banco de dados, contando com um modelo de objetos “pobre” não irão se
beneficiar com o uso do Hibernate.
Ele é mais indicado para sistemas que contam com um modelo rico, onde a
maior parte da lógica de domínios fica na própria aplicação Java, dependendo
pouco de funções específicas do banco de dados. Sua principal vantagem sobre
os seus concorrentes é a possibilidade de ser aplicado a bancos de dados
80
existentes, o que permite uma maior continuidade às aplicações legadas, além de
se integrar, via banco, a aplicações desenvolvidas em outras tecnologias. Tudo
isto, mantendo-se em conformidade com os padrões de desenvolvimento mais
atualizados.
8.1 – Considerações Finais
O Hibernate tem avançado bastante, e tem conquistado muito mercado.
Neste trabalho, foi mostrada apenas uma pequena parte desta solução, uma
sugestão para novos estudos seria o acompanhamento da alteração em uma
aplicação que usa EJB CMP para utilizar o Hibernate. Eles são frameworks muito
diferentes entre si, mas foram criados para uma mesma função. Como o EJB é
bastante popular e o Hibernate vem crescendo muito no mercado, a troca de uma
solução por outra é algo que pode e deve ser explorado para a melhoria dos
projetos de desenvolvimento orientados a objeto em Java.
Para este trabalho, não foi encontrada muita informação publicada sobre
este assunto, a grande maioria dos dados levantados foi conseguida por artigos
em revistas e publicações pela Internet. Existe a necessidade livros e publicações
que permitam fazer uma análise mais profunda este assunto, com uma base
teórica bem fundamentada e que possa comparar cada alternativa disponível, em
seus prós e contras.
Um outro estudo, vem da necessidade de desenvolvimento de novas
soluções integradas a sistemas legados. A partir de um sistema que esteja em
produção, e que fora desenvolvido para utilizar as características de um banco de
dados relacional, como fazer para desenvolver uma solução orientada a objetos
que funcione em paralelo com este outro sistema, e ao mesmo tempo, tenha toda
a desenvolvimento e a modelagem seguindo uma linha orientada a objetos, com o
mais avançado em termos de engenharia de software.
Este é um campo muito vasto para estudos, onde quanto mais se pesquisa
mais surgem novas possibilidades. Vivemos mundo, onde a maioria das
aplicações já foi desenvolvida, o grande desafio é como evoluir sem perder, ou
esquecer o passado.
81
REFERÊNCIAS BIBLIOGRÁFICAS:
1. LARMAN, C. – Utilizando UML e Padrões – Bookman, 2003;
2. MARTINS,V. – Persistência de Objetos – Bate Byte; Edição 90,
Setembro/ 1999;
http://www.pr.gov.br/batebyte/edicoes/1999/bb90/objetos.htm acessado em
23/05/05;
3. ALVIM, P & OLIVEIRA, H – Hibernate: Mapeamento OO x SGBDR – SQL
Magazine; Edição 2, Ano 1, 2003;
4. LOZANO, F - Persistência Objeto-Relacional com Java,
http://www.lozano.eti.br/palestras/persistencia-oo.pdf acessando em
23/05/2005;
5. ALUR, D.; CRUPI, J. & MALKS, D. – Core J2EE Patterns: As melhores
estratégia e práticas de design – Rio de Janeiro, Elsevier, 2004;
6. XEXEO, G. & LOPES, J. A. – Métodos de persistência em Java – SQL
Magazine; Edição 6, Ano 1, 2003;
7. OLIVEIRA, D - Livre-se do SQL: uma introdução ao Hibernate,
http://www.guj.com.br/java.tutorial.artigo.125.1.guj acessando em
02/08/2005;
8. BEZERRA, E. – Mapeando um modelo de classes para um banco de
dados relacional – SQL Magazine; Edição 5, Ano 1, 2003;
83
9. BOAGLIO, F. – Hibernate: O framework que veio para simplificar – SQL
Magazine; Edição 17, Ano 2, 2005;
10. BOAGLIO, F. – Hibernate: Facilitando o desenvolvimento Web – SQL
Magazine; Edição 19, Ano 2, 2005;
11. LINHARES, M - Introdução ao Hibernate 3,
http://www.guj.com.br/java.tutorial.artigo.174.1.guj acessando em
02/08/2005;
12. Hibernate Reference Documentation,
http://www.hibernate.org/hib_docs/v3/reference/en/pdf/hibernate_reference.
pdf acessando em 02/08/2005;
13. ELLIOTT, J - Working with Hibernate in Eclipse,
http://www.onjava.com/pub/a/onjava/2004/06/23/hibernate.html?page=1
acessando em 02/08/2005;
14. BAUER, C & KING, G - Get started with Hibernate: Introducing and
configuring Hibernate, http://www.javaworld.com/javaworld/jw-10-2004/jw1018-hibernate.html acessando em 02/08/2005;
15. About Middlegen, http://boss.bekk.no/boss/middlegen/ acessado em
02/09/2005;
16. About HiberClipse, http://hiberclipse.sourceforge.net/ acessado em
02/09/2005;
17. Hibernator, http://hibernator.sourceforge.net/ acessado em 02/09/2005;
18. Overview Hibernate Synchronizer, http://hibernatesynch.sourceforge.net/
acessando em 02/09/2005;
19. Hightower, R. – Object-relation mapping without the container,
http://www-128.ibm.com/developerworks/java/library/j-hibern/ acessando
em 05/09/2005;
20. Cheng, W. & Poddar, P. – Choose the Right Object Persistence,
http://www.fawcette.com/javapro/2003_07/online/wcheng_07_10_03/
acessando em 02/12/2005;
21. Cheng, W. & Poddar, P. – JDBC, CMP, or JDO?,
http://www.fawcette.com/javapro/2003_07/online/wcheng_07_25_03/
acessando em 02/12/2005;
84
22. Rocha, H. – Entrada e Saída,
http://www.argonavis.com.br/cursos/java/j100/java_15.pdf acessando em
05/12/2005;
ANEXO
ANEXO I: HIBERNATE: INSTALAÇÃO
Para se compilar e executar os exemplo descritos neste trabalho serão
necessários os seguintes softwares:

Ambiente J2SDK;

Hibernate;

Eclipse;

MySQL;

MySQL Administrator;

MySQL Query Browser;

Driver nativo JDBC para o MySQL (Conector/J);

Driver JDBC para o Oracle.
A.1 – Ambiente J2SDK
Para utilização do Java para o desenvolvimento é necessário o J2SDK. O
ambiente J2SDK utilizado foi à versão 1.5.0, mas pode ser utilizada qualquer
release
desde
a
1.4.2.
Ele
pode
ser
encontrado
no
endereço
http://java.sun.com/j2se/1.5.0/download.html. Siga a instruções até baixá-lo.
87
Uma vez baixado, basta abrir o executável e basta seguir as instruções que
ele mesmo faz todas as configurações necessárias.
A.2 – Hibernate
Agora se tem que realmente instalar o Hibernate. Neste trabalho se
utilizará a versão 3.0.5, mas pode ser utilizada uma versão superior. Ele pode se
encontrado no endereço http://www.hibernate.org/download. Siga a instruções até
baixá-lo.
Uma vez baixado, basta abrir o arquivo zipado e então extrai-lo em C:\. Ele
irá gerar uma nova pasta chamada C:\HIBERNATE_3.0.
A.3 – Eclipse
Precisa-se também de um ambiente de desenvolvimento para facilitar a
edição do código fonte. O ambiente de desenvolvimento utilizado foi o Eclipse
versão
3.1.0.
Ele
pode
se
encontrado
no
endereço
http://www.eclipse.org/download. Siga a instruções até baixá-lo.
Uma vez baixado, basta abrir o arquivo zipado e então extrai-lo em C:\. Ele
irá gerar uma nova pasta chamada C:\ECLIPSE.
O Eclipse possui a vantagem de possuir várias extensões que facilitam o
desenvolvimento utilizando a ferramenta Hibernate, em especial o HiberClipse
que
permite
ao
desenvolvedor
gerar
automaticamente os
arquivos
de
mapeamento. Além desta, as outras extensões podem ser também baixadas da
Internet, mas falaremos nelas a seguir.
A.4 – Servidor MySQL
Para o armazenamento físico dos dados precisa-se de uma banco de
dados relacional cliente/servidor, ou seja, que tenha suporte a linguagem SQL. O
88
banco de dados utilizado foi o MySQL versão 4.1.13. Ele pode ser baixado no
endereço http://www.mysql.org/download, siga a instruções até baixá-lo.
Uma vez baixado, basta abrir o executável e basta seguir as instruções que
ele mesmo faz todas as configurações necessárias.
A.5 – MySQL Administrator
Como se estará utilizando um banco de dados relacional, e neste caso, se
irá utilizar o MySQL, também se precisa de uma ferramenta para administração
que permita iniciar, configurar e gerenciar o banco de dados. Para este banco, ele
não vem com nenhuma ferramenta gráfica no pacote padrão. As ferramentas são
baixadas e instaladas separadamente. A ferramenta de administração utilizada foi
o MySQL Administrator versão 1.1.0. Ele pode ser baixado
no endereço
http://www.mysql.org/download, siga a instruções até baixá-lo.
Uma vez baixado, basta abrir o executável e basta seguir as instruções que
ele mesmo faz todas as configurações necessárias.
A.6 – MySQL Query Browser
Para facilitar a consulta dos dados armazenados no banco, se utilizará uma
ferramenta para consultas SQL. Assim pode-se conferir o que realmente esta indo
para o banco e como os dados ficaram armazenados. A ferramenta de consulta
SQL utilizada foi o MySQL Query Browser versão 1.1.13. Ele pode ser baixado
no endereço http://www.mysql.org/download, siga a instruções até baixá-lo.
Uma vez baixado, basta abrir o executável e basta seguir as instruções que
ele mesmo faz todas as configurações necessárias.
A.7 – Driver nativo JDBC para o MySQL (Conector/J);
89
Para acessar o MySQL pelo Java, vamos precisar do driver JDBC dele. Ele
pode ser baixado no endereço http://www.mysql.org/download, siga a instruções
até baixá-lo.
Uma vez baixado, basta abrir o arquivo zipado e extrailo em algum lugar.
Depois de feito isto, crie uma pasta chamada C:\JDBC e copie o arquivo .jar para
ela.
A.8 – Driver JDBC para o Oracle;
Para acessar o Oracle pelo Java, precisa-se do driver JDBC dele. Ele pode
ser baixado no endereço http://www.oracle.com/download, siga a instruções até
baixá-lo.
Uma vez baixado, basta abrir o arquivo zipado e extrailo em algum lugar.
Depois de feito isto, crie uma pasta chamada C:\JDBC e copie o arquivo .jar para
ela.
90
Download