1. Introdução
1.1. Objetivos
O PDF do documento está disponível |AQUI|.
Os exemplos do documento estão disponíveis |AQUI|.
O objetivo aqui é explorar os principais conceitos de persistência de dados utilizando a API JPA (Java Persistence API). Após ler este documento e testar os exemplos, o leitor deverá ter adquirido as bases necessárias para, posteriormente, poder avançar por conta própria.
A API JPA é relativamente nova. Só está disponível desde o JDK 1.5. A camada JPA tem o seu lugar numa arquitetura multicamadas. Consideremos uma arquitetura de três camadas bastante comum:
![]() |
- A camada [1], aqui referida como [ui] (Interface do Utilizador), é a camada que interage com o utilizador através de uma GUI Swing, uma interface de consola ou uma interface web. A sua função é fornecer dados do utilizador à camada [2] ou apresentar ao utilizador os dados fornecidos pela camada [2].
- A camada [2], aqui referida como [business], é a camada que aplica as chamadas regras de negócio — ou seja, a lógica específica da aplicação — sem se preocupar com a origem dos dados que recebe ou com o destino dos resultados que produz.
- A camada [3], aqui referida como [DAO] (Data Access Object), é a camada que fornece à camada [2] dados pré-armazenados (ficheiros, bases de dados, etc.) e armazena alguns dos resultados fornecidos pela camada [2].
- A camada [JDBC] é a camada padrão utilizada em Java para aceder a bases de dados. Esta é comummente designada por controlador JDBC do SGBD.
Foram envidados inúmeros esforços para facilitar aos programadores a escrita destas diferentes camadas. Entre estes, o JPA visa simplificar o desenvolvimento da camada [DAO], que gere os chamados dados persistentes, daí o nome da API (Java Persistence API). Uma solução que tem vindo a ganhar popularidade nos últimos anos neste campo é o Hibernate:
![]() |
A camada [Hibernate] situa-se entre a camada [DAO] escrita pelo programador e a camada [JDBC]. O Hibernate é um ORM (Mapeamento Objeto-Relacional), uma ferramenta que faz a ponte entre o mundo relacional das bases de dados e o mundo dos objetos manipulados pelo Java. O programador da camada [DAO] já não vê a camada [JDBC] nem as tabelas da base de dados cujo conteúdo pretende utilizar. Vê apenas a representação objeto da base de dados, fornecida pela camada [Hibernate]. A ponte entre as tabelas da base de dados e os objetos manipulados pela camada [DAO] é estabelecida principalmente de duas formas:
- através de ficheiros de configuração em estilo XML
- através de anotações Java no código, uma técnica disponível apenas a partir do JDK 1.5
A camada [Hibernate] é uma camada de abstração concebida para ser o mais transparente possível. O objetivo ideal é que o programador da camada [DAO] não tenha, de todo, consciência de que está a trabalhar com uma base de dados. Isto é viável se não for ele a escrever a configuração que faz a ponte entre o mundo relacional e o mundo dos objetos. Configurar esta ponte é bastante delicado e requer alguma experiência.
A camada de objetos [4], que espelha a base de dados, é chamada de «contexto de persistência». Uma camada [DAO] baseada no Hibernate executa operações de persistência (CRUD: criar, ler, atualizar, eliminar) nos objetos do contexto de persistência; estas operações são traduzidas pelo Hibernate em instruções SQL. Para operações de consulta à base de dados (SQL SELECT), o Hibernate fornece aos programadores uma HQL (Hibernate Query Language) para consultar o contexto de persistência [4] em vez da própria base de dados.
O Hibernate é popular, mas complexo de dominar. A curva de aprendizagem, frequentemente apresentada como fácil, é na verdade bastante íngreme. Assim que se tem uma base de dados com tabelas que apresentam relações um-para-muitos ou muitos-para-muitos, a configuração da ponte relacional-para-objeto está além das capacidades de um principiante médio. Erros de configuração podem então levar a aplicações com baixo desempenho.
No mundo comercial, existia um produto equivalente ao Hibernate chamado Toplink:
![]() |
À luz do sucesso dos produtos ORM, a Sun, criadora do Java, decidiu padronizar uma camada ORM através de uma especificação denominada JPA, lançada em conjunto com o Java 5. A especificação JPA foi implementada tanto pelo Toplink, da , como pelo Hibernate. O Toplink, que era um produto comercial, tornou-se entretanto de código aberto. Com o JPA, a arquitetura anterior passa a ser a seguinte:
![]() |
A camada [DAO] interage agora com a especificação JPA, um conjunto de interfaces. Os programadores beneficiaram desta padronização. Anteriormente, se um programador alterasse a sua camada ORM, também teria de alterar a sua camada [DAO], que tinha sido escrita para interagir com um ORM específico. Agora, escreverão uma camada [DAO] que interage com uma camada JPA. Independentemente do produto que implemente a camada JPA, a interface apresentada à camada [DAO] permanece a mesma.
Este documento apresentará exemplos de JPA em vários domínios:
- Primeiro, iremos focar-nos na ponte relacional/objeto que a camada ORM constrói. Esta será criada utilizando anotações Java 5 para bases de dados nas quais encontramos relações de tabelas do tipo:
- um-para-um
- um-para-muitos
- muitos-para-muitos
Para ilustrar esta área, iremos criar as seguintes arquiteturas de teste:
![]() |
Os nossos programas de teste serão aplicações de consola que consultam diretamente a camada JPA. Ao fazê-lo, exploraremos os principais métodos da camada JPA. Trabalharemos num ambiente denominado «Java SE» (Standard Edition). O JPA funciona tanto em ambientes Java SE como Java EE5 (Enterprise Edition).
- Assim que tivermos dominado tanto a configuração da ponte relacional/objeto como a utilização dos métodos da camada JPA, regressaremos a uma arquitetura multicamadas mais tradicional:
![]() |
A camada [JPA] será acedida através de uma arquitetura de duas camadas composta pelas camadas [negócio] e [DAO]. O framework Spring [7], seguido do contentor JBoss EJB3, será utilizado para ligar estas camadas entre si.
Mencionámos anteriormente que o JPA está disponível tanto em ambientes SE como EE5. O ambiente Java EE5 fornece inúmeros serviços para aceder a dados persistentes, incluindo pools de ligação, gestores de transações e muito mais. Pode ser vantajoso para um programador tirar partido destes serviços. O ambiente Java EE5 ainda não foi amplamente adotado (maio de 2007). Está atualmente disponível no Sun Application Server 9.x (Glassfish). Um servidor de aplicações é essencialmente um servidor de aplicações web. Se criar uma aplicação gráfica autónoma utilizando o Swing, não poderá utilizar o ambiente EE e os serviços que este fornece. Isto constitui um problema. Começamos a ver ambientes EE «autónomos», ou seja, aqueles que podem ser utilizados fora de um servidor de aplicações. É o caso do JBos EJB3, que iremos utilizar neste documento.
Num ambiente EE5, as camadas são implementadas por objetos chamados EJBs (Enterprise Java Beans). Nas versões anteriores do EE, os EJBs (EJB 2.x) eram considerados difíceis de implementar e testar e, por vezes, apresentavam um desempenho inferior ao esperado. É feita uma distinção entre beans «entidade» EJB 2.x e beans «sessão» EJB 2.x. Em resumo, uma «entidade» EJB 2.x corresponde a uma linha de uma tabela de base de dados, e uma «sessão» EJB 2.x é um objeto utilizado para implementar as camadas [negócio] e [DAO] de uma arquitetura multicamadas. Uma das principais críticas às camadas implementadas com EJBs é que estas só podem ser utilizadas dentro de contentores EJB, um serviço fornecido pelo ambiente EE. Isto torna os testes unitários problemáticos. Assim, no diagrama acima, os testes unitários das camadas [negócio] e [DAO] construídas com EJBs exigiriam a configuração de um servidor de aplicações, uma operação bastante complicada que não incentiva realmente o programador a realizar testes com frequência.
O framework Spring foi criado em resposta à complexidade do EJB2. O Spring fornece, num ambiente SE, um número significativo dos serviços normalmente fornecidos por ambientes EE. Assim, na secção «Persistência de Dados» que nos interessa aqui, o Spring fornece os pools de ligação e os gestores de transações de que as aplicações necessitam. O surgimento do Spring fomentou uma cultura de testes unitários, que de repente se tornaram muito mais fáceis de implementar. O Spring permite a implementação de camadas de aplicação utilizando objetos Java padrão (POJOs, Plain Old/Ordinary Java Objects), possibilitando a sua reutilização noutros contextos. Por fim, integra inúmeras ferramentas de terceiros de forma bastante transparente, nomeadamente ferramentas de persistência como o Hibernate, o iBatis, ...
O Java EE 5 foi concebido para resolver as lacunas da especificação EE anterior. O EJB 2.x evoluiu para o EJB 3. Trata-se de POJOs anotados com tags que os designam como objetos especiais quando se encontram dentro de um contentor EJB 3. Dentro do contentor, o EJB 3 pode tirar partido dos serviços do contentor (pool de ligações, gestor de transações, etc.). Fora do contentor EJB 3, o EJB 3 torna-se um objeto Java padrão. As suas anotações EJB são ignoradas.
Acima, representámos o Spring e o JBoss EJB3 como uma possível infraestrutura (framework) para a nossa arquitetura multicamadas. É esta infraestrutura que fornecerá os serviços de que precisamos: um pool de ligações e um gestor de transações.
- Com o Spring, as camadas serão implementadas utilizando POJOs. Estes irão aceder aos serviços do Spring (pool de ligações, gestor de transações) através da injeção de dependências nestes POJOs: ao construí-los, o Spring injeta referências aos serviços de que irão necessitar.
-
O JBoss EJB3 é um contentor EJB capaz de funcionar fora de um servidor de aplicações. O seu princípio de funcionamento (na perspetiva do programador) é análogo ao descrito para o Spring. Encontraremos poucas diferenças.
-
Concluiremos este documento com um exemplo de uma aplicação web de três camadas — básica, mas representativa:
![]() |
1.2. Referências
[ref1]: Java Persistence with Hibernate, de Christian Bauer e Gavin King, publicado pela Manning.
[ref1] é o documento que serviu de base para o que se segue. Trata-se de um livro abrangente com mais de 800 páginas sobre a utilização do ORM Hibernate em dois contextos diferentes: com ou sem JPA. A utilização do Hibernate sem JPA continua, de facto, a ser relevante para programadores que utilizam o JDK 1.4 ou versões anteriores, uma vez que o JPA só surgiu a partir do JDK 1.5.
Depois de ter lido mais de três quartos do livro e de ter dado uma vista de olhos pelo resto, ocorreu-me que tudo neste documento era útil. O utilizador experiente do Hibernate deve estar familiarizado com quase toda a informação fornecida nas 800 páginas. Christian Bauer e Gavin King foram minuciosos, mas raramente ao ponto de descrever situações com as quais nunca nos depararemos. Vale a pena ler tudo. O livro está escrito num estilo didático: há um esforço genuíno para não deixar nada por esclarecer. O facto de ter sido escrito para a utilização do Hibernate tanto com como sem JPA representa um desafio para quem está interessado apenas numa ou noutra destas tecnologias. Por exemplo, os autores descrevem, utilizando inúmeros exemplos, a ponte relacional/objeto em ambos os contextos. Os conceitos utilizados são muito semelhantes, uma vez que o JPA foi fortemente inspirado pelo Hibernate. Mas existem algumas diferenças. Tanto que algo que é verdade para o Hibernate pode já não ser verdade para o JPA, e isto acaba por criar confusão para o leitor.
Os autores fornecem exemplos de aplicações de três camadas no contexto de um contentor EJB3. Não abordam o Spring. Veremos num exemplo que o Spring é, no entanto, mais simples de utilizar e tem um âmbito mais alargado do que o contentor JBoss EJB3 utilizado em [ref1]. No entanto, «Java Persistence with Hibernate» é um excelente livro que recomendo por todos os fundamentos que ensina sobre ORMs.
Usar um ORM é complexo para um principiante.
- Há conceitos a compreender para configurar a ponte relacional/objeto.
- Existe o conceito de contexto de persistência com as suas noções de objetos num estado «persistido», «desligado» ou «novo»
- Existem os mecanismos em torno da persistência (transações, pools de conexão), tipicamente serviços fornecidos por um contêiner
- Há configurações relacionadas com o desempenho a definir (cache de segundo nível)
- ...
Iremos apresentar estes conceitos através de exemplos. Não iremos aprofundar a teoria subjacente. O nosso objetivo é simplesmente, em cada caso, permitir que o leitor compreenda o exemplo e o interiorize ao ponto de poder fazer modificações por si próprio ou aplicá-lo num contexto diferente.
1.3. Ferramentas utilizadas
Os exemplos neste documento utilizam as seguintes ferramentas. Algumas são descritas nos apêndices (transferência, instalação, configuração, utilização). Nesses casos, indicamos o número do parágrafo e o número da página.
- um JDK 1.6 (secção 5.1)
- o IDE de desenvolvimento Java Eclipse 3.2.2 (secção 5.2)
- Plugin Eclipse WTP (Web Tools Package) (secção 5.2.3)
- Plugin Eclipse SQL Explorer (secção 5.2.6)
- o plugin Eclipse Hibernate Tools (secção 5.2.5)
- Plugin Eclipse TestNG (secção 5.2.4)
- Container de servlets Tomcat 5.5.23 (secção 5.3)
- SGBD Firebird 2.1 (secção 5.4)
- SGBD MySQL 5 (Secção 5.5)
- SGBD PostgreSQL (secção 5.6)
- SGBD Oracle 10g Express (secção 5.7)
- SGBD SQL Server 2005 Express (secção 5.8)
- SGBD HSQLDB (Secção 5.9)
- SGBD Apache Derby (Secção 5.10)
- Spring 2.1 (Secção 5.11)
- Container JBoss EJB3 (Secção 5.12)
1.4. Descarregar o exemplo e-
No site deste documento, os exemplos abordados podem ser descarregados como um ficheiro ZIP que, uma vez extraído, cria a seguinte pasta:
![]() |
- em [1]: a estrutura de diretórios dos exemplos
- em [2]: a pasta <annexes> contém os itens apresentados na secção ANEXOS, parágrafo 5. Em particular, a pasta <jdbc> contém os controladores JDBC para os SGBDs utilizados nos exemplos do tutorial.
- em [3]: a pasta <lib> agrupa os vários arquivos .jar utilizados pelo tutorial em 5 pastas
- [4]: A pasta <lib/divers> contém os arquivos: - drivers JDBC para o SGBD - para a ferramenta de testes unitários [TestNG] - a ferramenta de registo [log4j]
![]() |
- em [5]: os arquivos da implementação JPA/Hibernate e das ferramentas de terceiros necessárias para o Hibernate
- em [6]: os arquivos para a implementação JPA/TopLink
- em [7]: os arquivos do Spring 2.x e as ferramentas de terceiros necessárias para o Spring
- em [8]: os arquivos do contêiner JBoss EJB3
![]() |
- em [9]: a pasta <hibernate> contém exemplos que utilizam a camada de persistência JPA/Hibernate
- em [10]: a pasta <hibernate/direct> contém exemplos em que a camada JPA é utilizada diretamente com um programa do tipo [Main].
- em [11] e [12]: exemplos em que a camada JPA é utilizada através das camadas [business] e [DAO] numa arquitetura multicamadas, que é o caso de utilização padrão. Os serviços (pool de ligações, gestor de transações) utilizados pelas camadas [business] e [DAO] são fornecidos pelo Spring [11] ou pelo JBoss EJB3 [12].
![]() |
- Em [13]: A pasta <toplink> inclui os exemplos da pasta <hibernate> [9], mas desta vez com uma camada de persistência JPA/Toplink em vez de JPA/Hibernate. Não existe uma pasta <jbossejb3> em [13] porque não foi possível obter um exemplo funcional em que a camada de persistência fosse fornecida pelo Toplink e os serviços pelo contentor JBoss EJB3.
- Em [14]: uma pasta <web> contém três exemplos de aplicações web com uma camada de persistência JPA:
- [15]: um exemplo que utiliza Spring / JPA / Hibernate
- [16]: o mesmo exemplo com Spring / JPA / Toplink
- [17]: o mesmo exemplo com JBoss EJB3 / JPA / Hibernate. Este exemplo não funciona, provavelmente devido a um problema de configuração não resolvido. No entanto, foi incluído para que o leitor possa examiná-lo e, possivelmente, encontrar uma solução para este problema.
O tutorial refere-se frequentemente a esta estrutura de diretórios, particularmente ao testar os exemplos abordados. Recomenda-se aos leitores que descarreguem estes exemplos e os instalem. Daqui em diante, referir-nos-emos à estrutura de diretórios descrita acima como <examples>.
1.5. -Configuração do projeto Eclipse para os exemplos
Os exemplos utilizam bibliotecas «user». Estas são arquivos .jar agrupados sob um único nome. Quando uma biblioteca deste tipo é incluída no classpath de um projeto Java, todos os arquivos que ela contém são então incluídos nesse classpath. Vamos ver como fazer isto no Eclipse:
![]() |
- em [1]: [Janela / Preferências / Java / Caminho de Compilação / Bibliotecas do Utilizador]
- em [2]: crie uma nova biblioteca
- em [3]: atribua-lhe um nome e confirme
![]() |
- em [4]: selecione os JARs que farão parte da biblioteca [jpa-divers]
- em [5]: selecione todos os JARs da pasta <examples>/lib/divers
![]() |
- em [6]: a biblioteca do utilizador [jpa-divers] foi definida
- em [7]: repita o mesmo processo para criar mais 4 bibliotecas:
Biblioteca | Pasta JAR da biblioteca |
<examples>/lib/hibernate | |
<exemplos>/lib/toplink | |
<exemplos>/lib/spring | |
<exemplos>/lib/jbossejb3 |













