1. Introdução
1.1. Objectifs
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 com o API JPA (Java Persistence API). Após ler este documento e testar os exemplos, o leitor deverá ter adquirido as bases necessárias para, a partir daí, seguir por conta própria.
A API JPA é recente. Ela só ficou disponível a partir da versão 1.5 da JDK. A camada JPA tem seu lugar em uma arquitetura multicamadas. Consideremos uma arquitetura bastante comum, a de três camadas:
![]() |
- a camada [1], aqui denominada [ui] (Interface do Usuário), é a camada que interage com o usuário, por meio de uma interface gráfica Swing, uma interface de console ou uma interface web. Sua função é fornecer dados provenientes do usuário à camada [2] ou apresentar ao usuário os dados fornecidos pela camada [2].
- A camada [2], aqui denominada [metier], é a camada que aplica as regras de negócio, c.a.d. a lógica específica da aplicação, sem se preocupar com a origem dos dados que lhe são fornecidos nem com o destino dos resultados que ela produz.
- a camada [3], aqui denominada [dao] (Data Access Object), é a camada que fornece à camada [2] dados pré-registrados (arquivos, bancos de dados, ...) e que grava alguns dos resultados fornecidos pela camada [2].
- A camada [JDBC] é a camada padrão utilizada em Java para acessar bancos de dados. É o que se costuma chamar de driver JDBC do SGBD.
Vários esforços foram realizados para facilitar a criação dessas diferentes camadas pelos desenvolvedores. Entre eles, o JPA visa facilitar a criação da camada [dao], que gerencia os dados chamados de persistentes, daí o nome da API (Java Persistence API). Uma solução que ganhou destaque nos últimos anos nessa área é a do Hibernate:
![]() |
A camada [Hibernate] situa-se entre a camada [dao], escrita pelo desenvolvedor, e a camada [Jdbc]. O Hibernate é um ORM (Mapeamento Objeto-Relacional), uma ferramenta que faz a ponte entre o mundo relacional dos bancos de dados e o mundo dos objetos manipulados pelo Java. O desenvolvedor da camada [dao] não vê mais a camada [Jdbc] nem as tabelas do banco de dados cujo conteúdo deseja explorar. Ele vê apenas a representação objeto do banco de dados, fornecida pela camada [Hibernate]. A ponte entre as tabelas do banco de dados e os objetos manipulados pela camada [dao] é estabelecida principalmente de duas maneiras:
- por meio de arquivos de configuração do tipo XML
- por meio de anotações Java no código, técnica disponível apenas a partir da versão 1.5 da camada JDK
A camada [Hibernate] é uma camada de abstração que se propõe a ser a mais transparente possível. O ideal é que o desenvolvedor da camada [dao] possa ignorar completamente o fato de estar trabalhando com um banco de dados. Isso é possível se não for ele quem escrever a configuração que faz a ponte entre o mundo relacional e o mundo dos objetos. A configuração dessa ponte é bastante delicada e requer certa familiaridade.
A camada [4] dos objetos, espelho da BD, é chamada de “contexto de persistência”. Uma camada [dao] baseada no Hibernate realiza ações de persistência (CRUD: criar, ler, atualizar, excluir) nos objetos do contexto de persistência; essas ações são traduzidas pelo Hibernate em comandos SQL. Para as ações de consulta ao banco de dados (o SQL Select), o Hibernate fornece ao desenvolvedor uma linguagem HQL (Hibernate Query Language) para consultar o contexto de persistência [4], e não a própria BD.
O Hibernate é popular, mas complexo de dominar. A curva de aprendizado, frequentemente apresentada como fácil, é, na verdade, bastante íngreme. Assim que se tem um banco de dados com tabelas que possuem relações um-para-muitos ou muitos-para-muitos, a configuração da ponte relacional/objetos não está ao alcance de qualquer iniciante. Erros de configuração podem, então, levar a aplicativos com baixo desempenho.
No mundo comercial, existia um produto equivalente ao Hibernate chamado Toplink:
![]() |
Diante do sucesso dos produtos ORM, a Sun, criadora do Java, decidiu padronizar uma camada ORM por meio de uma especificação chamada JPA, lançada ao mesmo tempo que o Java 5. A especificação JPA foi implementada pelos dois produtos Toplink e Hibernate. O Toplink, que era um produto comercial, tornou-se desde então um produto de código aberto. Com o JPA, a arquitetura anterior passa a ser a seguinte:
![]() |
A camada [dao] agora interage com a especificação JPA, um conjunto de interfaces. O desenvolvedor ganhou em termos de padronização. Antes, se ele alterasse sua camada ORM, precisava também alterar sua camada [dao], que havia sido escrita para interagir com um ORM específico. Agora, ele escreverá uma camada [dao] que se comunicará com uma camada JPA. Independentemente do produto que a implemente, a interface da camada JPA apresentada à camada [dao] permanece a mesma.
Este documento apresentará exemplos JPA em diversas áreas:
- em primeiro lugar, vamos nos concentrar na ponte relacional/objeto que a camada ORM constrói. Ela será criada por meio de anotações Java 5 para bancos de dados nos quais existem relações entre tabelas do tipo:
- um a um
- um para vários
- muitos para muitos
Para ilustrar essa área, criaremos as seguintes arquiteturas de teste:
![]() |
Nossos programas de teste serão aplicativos de console que consultarão diretamente a camada JPA. Nessa ocasião, conheceremos os principais métodos da camada JPA. Estaremos em um ambiente denominado “Java SE” (Standard Edition). O JPA funciona tanto no ambiente Java SE quanto no Java EE5 (Enterprise Edition).
- Quando dominarmos tanto a configuração da ponte relacional/objeto quanto o uso dos métodos da camada JPA, voltaremos a uma arquitetura multicamadas mais clássica:
![]() |
A camada [JPA] será acessada por meio de uma arquitetura de duas camadas: [metier] e [dao]. O framework Spring [7] e, em seguida, o contêiner EJB3 de JBoss serão utilizados para interligar essas camadas entre si.
Mencionamos anteriormente que o JPA estava disponível nos ambientes SE e EE5. O ambiente Java EE5 oferece diversos serviços na área de acesso a dados persistentes, incluindo pools de conexão, gerenciadores de transações, etc. Pode ser interessante para um desenvolvedor aproveitar esses serviços. O ambiente Java EE5 ainda não é muito difundido (maio de 2007). Atualmente, ele está disponível no servidor de aplicativos Sun Application Server 9.x (Glassfish). Um servidor de aplicativos é, essencialmente, um servidor de aplicativos web. Se for desenvolvida uma aplicação gráfica autônoma do tipo Swing, não é possível dispor do ambiente EE e dos serviços que ele oferece. Isso é um problema. Começam a surgir ambientes EE “autônomos”, c.a.d. que podem ser utilizados fora de um servidor de aplicativos. É o caso do JBos e do EJB3, que utilizaremos neste documento.
Em um ambiente EE5, as camadas são implementadas por objetos chamados EJB (Enterprise Java Bean). Nas versões anteriores do EE, os EJB (EJB e 2.x) eram considerados difíceis de implementar, testar e, às vezes, de baixo desempenho. Distinguem-se os EJB2.x “entity” e os EJB2.x “session”. Resumindo, um EJB2.x “entity” é a representação de uma linha de uma tabela de banco de dados e um EJB2.x “session” é um objeto utilizado para implementar as camadas [metier], [dao] de uma arquitetura multicamadas. Uma das principais críticas feitas às camadas implementadas com EJB é que elas só podem ser utilizadas dentro de contêineres EJB, um serviço fornecido pelo ambiente EE. Isso dificulta a realização de testes unitários. Assim, no esquema acima, os testes unitários das camadas [metier] e [dao], construídas com EJB, exigiriam a instalação de um servidor de aplicativos, uma operação bastante trabalhosa que não incentiva muito o desenvolvedor a realizar testes com frequência.
O framework Spring surgiu como resposta à complexidade dos EJB2. O Spring fornece, em um ambiente SE, um número significativo dos serviços normalmente oferecidos pelos ambientes EE. Assim, na parte “Persistência de dados” que nos interessa aqui, o Spring fornece os pools de conexão e os gerenciadores de transações de que as aplicações precisam. O surgimento do Spring favoreceu a cultura dos testes unitários, que de repente se tornaram muito mais fáceis de implementar. O Spring permite a implementação das camadas de uma aplicação por meio de objetos Java clássicos (POJO, Plain Old/Ordinary Java Object), possibilitando a reutilização desses objetos em outro contexto. Por fim, ele integra diversas ferramentas de terceiros de maneira bastante transparente, notadamente ferramentas de persistência como Hibernate, Ibatis, ...
O Java EE5 foi concebido para corrigir as lacunas da especificação anterior, EE. Os EJB e 2.x passaram a ser os EJB3. Estes são POJOs marcados por anotações que os tornam objetos específicos quando estão dentro de um contêiner EJB3. Nesse contêiner, o EJB3 poderá se beneficiar dos serviços do contêiner (pool de conexões, gerenciador de transações, etc.). Fora do contêiner EJB3, o EJB3 torna-se um objeto Java normal. Suas anotações EJB são ignoradas.
Acima, representamos o Spring e o JBoss EJB3 como uma possível infraestrutura (framework) para nossa arquitetura multicamadas. É essa infraestrutura que fornecerá os serviços de que precisamos: um pool de conexões e um gerenciador de transações.
- Com o Spring, as camadas serão implementadas com POJOs. Esses componentes terão acesso aos serviços do Spring (pool de conexões, gerenciador de transações) por meio da injeção de dependências nesses POJOs: durante a construção deles, o Spring injeta referências aos serviços de que eles precisarão.
-
O JBoss EJB3 é um contêiner EJB capaz de funcionar fora de um servidor de aplicativos. Seu princípio de funcionamento (para o desenvolvedor) é análogo ao descrito para o Spring. Encontraremos poucas diferenças.
-
Concluiremos o documento com um exemplo de aplicação web de três camadas, básica, mas, mesmo assim, representativa:
![]() |
1.2. Références
[ref1]: Java Persistence with Hibernate, de Christian Bauer e Gavin King, editora 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 o uso do ORM Hibernate em dois contextos diferentes: com ou sem JPA. O uso do Hibernate sem o JPA continua, de fato, sendo relevante para desenvolvedores que utilizam o JDK 1.4 ou versões anteriores, já que o JPA só surgiu com o JDK 1.5.
Depois de ler mais de três quartos do livro e dar uma olhada no restante, percebi que tudo era útil nesse documento. O usuário experiente do Hibernate deve conhecer praticamente todas as informações apresentadas nas 800 páginas. Christian Bauer e Gavin King foram exaustivos, mas raramente descreveram situações que nunca encontraremos. Vale a pena ler tudo. O livro foi escrito de forma didática: há uma vontade genuína de não deixar nada na obscuridade. O fato de ter sido escrito para o uso do Hibernate tanto com quanto sem o JPA representa uma dificuldade para aqueles que estão interessados apenas em uma ou outra dessas tecnologias. Por exemplo, os autores descrevem, por meio de numerosos exemplos, a ponte relacional/objeto nos dois contextos. Os conceitos utilizados são muito semelhantes, já que o JPA se inspirou fortemente no Hibernate. No entanto, apresentam algumas diferenças. De modo que algo que é válido para o Hibernate pode não ser mais válido para o JPA, o que acaba gerando confusão no leitor.
Os autores apresentam exemplos de aplicações de três camadas no contexto de um contêiner EJB3. Eles não mencionam o Spring. Veremos, por meio de um exemplo, que o Spring é, no entanto, mais simples de usar e tem um escopo mais abrangente do que o contêiner JBoss EJB3 utilizado no [ref1]. No entanto, “Java Persistence with Hibernate” é um excelente livro que recomendo por todos os conceitos básicos que ele ensina sobre os ORM.
Usar um ORM é complexo para um iniciante.
- Há conceitos que precisam ser compreendidos para configurar a ponte relacional/objeto.
- Há a noção de contexto de persistência com seus conceitos de objetos em um estado “persistente”, “desanexado” e “novo”
- há a mecânica em torno da persistência (transações, pools de conexões), geralmente serviços fornecidos por um contêiner
- Há ajustes a serem feitos para otimizar o desempenho (cache de segundo nível)
- ...
Apresentaremos esses conceitos por meio de exemplos. Faremos poucas explicações teóricas sobre eles. Nosso objetivo é simplesmente, a cada vez, permitir que o leitor compreenda o exemplo e o assimile a ponto de ser capaz de fazer alterações por conta própria ou de reaplicá-lo em outro contexto.
1.3. Ferramentas utilizadas
Os exemplos deste documento utilizam as seguintes ferramentas. Algumas delas são descritas nos anexos (download, instalação, configuração, uso). Nesses casos, são indicados o número do parágrafo e a página.
- um JDK 1.6 (parágrafo 5.1)
- o IDE para desenvolvimento em Java no Eclipse 3.2.2 (parágrafo 5.2)
- plug-in do Eclipse WTP (Web Tools Package) (parágrafo 5.2.3)
- plug-in do Eclipse SQL Explorer (parágrafo 5.2.6)
- plug-in Eclipse Hibernate Tools (parágrafo 5.2.5)
- plug-in do Eclipse TestNG (parágrafo 5.2.4)
- contêiner de servlets Tomcat 5.5.23 (parágrafo 5.3)
- SGBD Firebird 2.1 (parágrafo 5.4)
- SGBD MySQL5 (parágrafo 5.5)
- SGBD PosgreSQL (parágrafo 5.6)
- SGBD Oracle 10g Express (parágrafo 5.7)
- SGBD SQL Server 2005 Express (parágrafo 5.8)
- SGBD HSQLDB (parágrafo 5.9)
- SGBD Apache Derby (parágrafo 5.10)
- Spring 2.1 (parágrafo 5.11)
- contêiner EJB3 de JBoss (parágrafo 5.12)
1.4. Download dos exemplos do
No site deste documento, os exemplos analisados podem ser baixados na forma de um arquivo zip que, uma vez descompactado, gera a seguinte pasta:
![]() |
- em [1]: a estrutura dos exemplos
- em [2]: a pasta <annexes> contém os elementos apresentados na seção ANNEXES, parágrafo 5. Em particular, a pasta <jdbc> contém os drivers JDBC do SGBD utilizados nos exemplos do tutorial.
- em [3]: a pasta <lib> agrupa, em 5 pastas, os diversos arquivos .jar utilizados pelo tutorial
- no [4]: a pasta <lib/divers> agrupa os arquivos: - dos drivers JDBC do SGBD - da ferramenta de teste unitário [testNG] - da ferramenta de logs [log4j]
![]() |
- em [5]: os arquivos da implementação JPA/Hibernate e das ferramentas de terceiros necessárias para o Hibernate
- em [6]: os arquivos da implementação JPA/Toplink
- em [7]: os arquivos do Spring 2.x e das ferramentas de terceiros necessárias para o Spring
- em [8]: os arquivos do contêiner EJB3 de JBoss
![]() |
- em [9]: a pasta <hibernate> reúne os exemplos tratados com a camada de persistência JPA/Hibernate
- em [10]: a pasta <hibernate/direct> reúne os 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 por meio das camadas [metier] e [dao] em uma arquitetura multicamadas, o que constitui o caso normal de utilização. Os serviços (pool de conexões, gerenciador de transações) utilizados pelas camadas [metier] e [dao] são fornecidos pelo Spring [11] ou pelo JBoss EJB3 [12].
![]() |
- em [13]: a pasta <toplink> retoma os exemplos da pasta <hibernate> [9], mas, desta vez, com uma camada de persistência JPA/Toplink em vez de JPA/Hibernate. Nãoexiste, no [13], nenhuma pasta <jbossejb3>, pois não foi possível fazer funcionar um exemplo em que a camada de persistência fosse fornecida pelo Toplink e os serviços fossem fornecidos pelo contêiner EJB3 do JBoss.
- Em [14]: uma pasta <web> reúne três exemplos de aplicativos web com uma camada de persistência JPA:
- [15]: um exemplo com Spring / JPA / Hibernate
- [16]: o mesmo exemplo com Spring / JPA / Toplink
- [17]: o mesmo exemplo com JBoss, EJB3 e JPA / Hibernate. Este exemplo não funciona, provavelmente devido a um problema de configuração ainda não esclarecido. No entanto, ele foi mantido para que o leitor possa analisá-lo e, eventualmente, encontrar uma solução para esse problema.
O tutorial frequentemente faz referência a essa estrutura de diretórios, principalmente durante os testes dos exemplos estudados. O leitor é convidado a baixar esses exemplos e instalá-los. Daqui em diante, chamaremos de <exemplos> a estrutura de diretórios dos exemplos descrita acima.
1.5. Configuração dos projetos Eclipse dos exemplos
Os exemplos utilizam bibliotecas “de usuário”. Trata-se de arquivos .jar agrupados sob um mesmo nome. Ao incluir uma biblioteca desse tipo no classpath de um projeto Java, todos os arquivos que ela contém passam a fazer parte desse classpath. Vejamos como fazer isso no Eclipse:
![]() |
- no [1]: [Window / Preferences / Java / Buld Path / User Libraries]
- no [2]: cria-se uma nova biblioteca
- em [3]: atribuímos um nome e confirmamos
![]() |
- em [4]: vamos selecionar os arquivos JAR que farão parte da biblioteca [jpa-divers]
- em [5]: selecionamos todos os arquivos JAR da pasta <exemplos>/lib/divers
![]() |
- em [6]: a biblioteca de usuário [jpa-divers] foi definida
- em [7]: repete-se o mesmo procedimento para criar outras 4 bibliotecas:
Biblioteca | Pasta dos arquivos JAR da biblioteca |
<exemplos>/lib/hibernate | |
<exemplos>/lib/toplink | |
<exemplos>/lib/spring | |
<exemplos>/lib/jbossejb3 |













