Esse artigo faz parte da revista Java Magazine edição 39. Clique aqui para ler todos os artigos desta edição

imagem

Clique aqui para ler todos os artigos desta ediçãoimagem_pdf.jpg

Persistência no Java EE 5

Iniciando com a Java Persistence API

Aprenda a utilizar a nova API de persistência do Java EE 5 através de um exemplo completo

O principal foco da versão 5 do Java EE é a facilidade de uso. O novo Java EE é bem mais simples que sua versão anterior e suas novas APIs aumentam a produtividade dos desenvolvedores, exigindo menos esforço de codificação. Uma das principais novidades do Java EE 5 é a nova API de persistência. Há muito tempo esperada pela comunidade Java, a Java Persistence API (JPA) padroniza as operações de persistência sobre entidades Java, definindo uma especificação para mapeamento objeto-relacional. Neste artigo apresentaremos a JPA através da criação de um exemplo completo.

Persistência e mapeamento O/R

Como sabemos, a tecnologia de banco de dados relacionais existe há décadas, e hoje os SGBDs são robustos e confiáveis. Os principais problemas relacionados ao armazenamento e recuperação de dados já foram solucionados, e o investimento das empresas nesses sistemas também é imenso, o que torna a sua utilização uma regra.

O uso dos bancos de dados relacionais, no entanto, traz alguns inconvenientes ao desenvolvedor em linguagens orientadas a objetos como Java. As tecnologias OO e relacional são bastante diferentes, e seu uso conjunto normalmente implica em enfatizar uma tecnologia em sacrifício da outra. Como os bancos de dados OO ainda não estão muito menos disseminados que os bancos de dados relacionais, o desafio atual dos desenvolvedores é unir dois mundos completamente distintos, utilizando a tecnologia relacional para armazenar objetos.

O armazenamento de objetos de uma aplicação é denominado persistência de objetos. Essa técnica permite que as instâncias existentes no sistema sejam armazenadas e posteriormente recuperadas, conservando-se o seu estado mesmo após a aplicação ter sido finalizada.

Abordagens atuais de mapeamento

Desde as suas primeiras versões, a plataforma Java oferece acesso a bancos de dados através da API JDBC, que trabalha no mesmo nível do banco, sendo o acesso às informações armazenadas feito através de comandos SQL. Em muitos aspectos, a JDBC é uma API de baixo nível, que muitas vezes exige do desenvolvedor o conhecimento das “nuances” do banco de dados. Apesar de ser uma maneira eficiente de acessar dados em SGBDs relacionais, e a opção que normalmente oferece melhor performance, a JDBC oferece uma abstração OO bastante limitada (trabalha-se com tabelas, registros e resultsets, ao invés de objetos).

Para usar os recursos de bancos de dados relacionais em Java e ainda assim aproveitar os conceitos do paradigma OO, é necessário fazer o que se conhece como mapeamento objeto-relacional (ou simplesmente mapeamento O/R). No mapeamento O/R as classes e os atributos do sistema são mapeados para tabelas e campos/colunas, e a persistência é feita de forma transparente pela aplicação. Assim, objetos em memória são armazenados no banco, e objetos do banco são trazidos para a memória sempre que necessário. Com paradigmas tão diferentes, diversas questões surgem durante o mapeamento: Como mapear herança? E agregação? Cada classe deve virar uma tabela? Como aproveitar os recursos do banco sem perder a abstração de objetos?

Para suprir essas necessidades, surgiram diversos frameworks e tecnologias de persistência, a exemplo do Hibernate e do iBatis. Essas ferramentas facilitam o trabalho do desenvolvedor e aumentam sua produtividade, fornecendo poderosas APIs de manipulação de dados. Apesar de cada ferramenta possuir uma forma distinta de efetuar o mapeamento O/R, os conceitos são semelhantes e relativamente simples, baseando-se em POJOs (Plain Old Java Objects).

 O termo Plain Java Old Object (ou simplesmente, POJO) foi criado por Martin Fowler, Rebecca Parsons e Josh MacKenzie em 2000. A tradução é semelhante a “velho e bom objeto Java” e refere-se a objetos/classes Java simples, não atrelados a ferramentas ou frameworks

A Java Persistence API

Até o J2EE 1.4 a plataforma Java não possuía uma forma simples de mapear objetos no banco de dados. A única opção era a utilização de Entity Beans, que exigem um container EJB e possuem uma complexidade razoável . Aplicações cuja arquitetura não envolvia EJBs precisavam utilizar ferramentas não padronizadas como o Hibernate para fazer a persistência, ou fazer a implementação de persistência manualmente.
A Java Persistence API, definida na JSR-220 (Enterprise JavaBeans,Version 3.0), padroniza o mapeamento objeto-relacional na plataforma Java. Apesar de descrita na especificação do novo EJB, a JPA não depende de um container para funcionar, sendo possível usar e testar soluções apenas com o Java SE .
A JPA é uma especificação baseada no conceito de POJOs, que incorpora idéias de renomados frameworks de persistência para padronizar o mapeamento O/R em Java. A API oferece uma solução completa para mapeamento e persistência de objetos:
• Um modo declarativo de descrever mapeamentos O/R
• Uma linguagem de consulta
• Um conjunto de ferramentas para manipular entidades

Em se tratando de um padrão do Java Community Process, a JPA traz diversos benefícios. O uso de um padrão para a persistência de objetos permite que diversos fabricantes trabalhem sobre os mesmos conceitos e que o desenvolvedor escolha a implementação de sua preferência. A padronização também traz outra importante vantagem: pode-se alternar entre implementações de fabricantes distintos, que estejam em conformidade com a JSR-220, sem nenhum esforço adicional. Dessa forma, uma aplicação codificada de acordo com o novo padrão irá funcionar com qualquer implementação da JPA, não havendo necessidade de se conhecer (a princípio) qual tecnologia será utilizada para essa implementação.

Conceitos básicos

Na JPA os objetos persistentes são denominados entidades (entities). Uma entidade é um objeto simples (POJO), que representa um conjunto de dados persistido no banco. Como entidades são definidas por classes Java comuns, sem relação com frameworks ou bibliotecas, elas podem ser abstratas ou herdar de outras classes, sem restrições.
Um conceito importante é que as entidades possuem um identificador (descrito pela chave primária) e estado, sendo seu tempo de vida independente do tempo de vida da aplicação. Assim, aplicações distintas podem compartilhar a mesma entidade, que é referenciada através de seu identificador.

Para que uma entidade se torne persistente é necessário associá-la a um persistence context (“contexto de persistência”), que fornece a conexão entre as instâncias e o banco de dados. A manipulação das entidades é feita, a partir desse contexto, por meio do entity manager (“gerenciador”), que é responsável por executar as operações básicas sobre a entidade (criação, atualização, exclusão, localização, consultas etc.). O entity manager na JPA é uma instância da interface javax.persistence.EntityManager.

A implementação da JPA é feita por um persistence provider (“provedor”). O provedor define “como as coisas funcionam”, através da implementação de todas as interfaces definidas pela especificação da JPA. Dessa forma, cada provedor decide a maneira e o momento de carregar, atualizar e armazenar as entidades, assim como sincronizar os dados com o banco. As configurações utilizadas pelo provedor em uma determinada aplicação (conexão com o banco de dados, entidades que serão gerenciadas, tipo de transação etc.) são descritas em uma persistence unit, que é configurada num arquivo especial denominado persistence.xml.

 

Mapeamento

As classes e interfaces da JPA estão localizadas no pacote javax.persistence. A API faz uso intensivo de anotações ; por isso não é necessário criar descritores XML para cada uma das entidades da aplicação . Uma entidade é uma classe Java comum, rotulada através da anotação @Entity. Não é necessário implementar interfaces ou estender outras classes para tornar uma classe “persistível”; a única exigência é que a classe da entidade possua um construtor sem parâmetros, pois a instanciação da classe é feita por reflexão.
No código a seguir a classe Pessoa representa uma entidade. O atributo cpf é o identificador da entidade (chave primária), especificado através da anotação @Id:

@Entity
public class Pessoa {
  @Id
  private String cpf;
}

Grande parte da produtividade trazida pela JPA deve-se à utilização de valores default de mapeamento, que facilitam bastante o trabalho do desenvolvedor. Assim, o que não é definido explicitamente assume a configuração padrão da API. Por exemplo, por padrão a JPA considera o nome da entidade o mesmo nome da tabela no banco de dados e o nome da propriedade o mesmo nome da coluna. No código anterior, a entidade Pessoa será salva na tabela PESSOA e a propriedade cpf na coluna CPF. Caso seja necessário alterar a forma de mapeamento, devem-se utilizar as anotações @Table e @Column, por exemplo:

@Entity
@Table(name=”TB_PESSOA”)
public class Pessoa {
  @Id
  @Column(name=”DS_CPF)”
  private String cpf;
}

Outro padrão utilizado pelo JPA é considerar todas as propriedades de uma entidade como persistentes (o mapeamento segue as regras descritas anteriormente). Caso seja desejável excluir alguma propriedade do mapeamento (por exemplo, no caso de ela poder ser criada a partir de outras propriedades), basta marcá-la com a anotação @Transient:

@Entity
public class Pessoa {
  @Id
  private String cpf;
  @Transient
  private String nomeCompleto;  
}

Os dados de uma única entidade podem estar distribuídos em mais de uma tabela, e diversos tipos de relacionamentos entre entidades são possíveis. Os mais comuns são os de agregação (anotações @OneToOne, @OneToMany, @ManyToOne, @ManyToMany, etc.) e herança (anotação @Inheritance), que serão vistos mais adiante.

Consultas

A JPA oferece suporte a consultas estáticas e dinâmicas. A API fornece uma linguagem própria de consulta, uma extensão bastante poderosa da EJB QL (a linguagem de consultas do EJB). Essa linguagem pode ser usada como uma alternativa ao SQL, que também é suportado. As consultas suportam polimorfismo, o que significa que quando uma entidade é consultada, todas as entidades descendentes que atendam ao critério da consulta também são retornadas. A criação de consultas é feita através do EntityManager, que fornece métodos específicos para instanciar consultas estáticas e dinâmicas, além de permitir a execução das operações CRUD .
As consultas estáticas possuem nomes e são descritas pela anotação @NamedQuery. Elas são definidas nas entidades correspondentes e ficam “pré-compiladas”. Veja um exemplo de consulta estática para localizar uma pessoa pelo seu CPF:

@NamedQuery(name = “consultarPorCPF”,
  query = “SELECT p FROM Pessoa p WHERE p.cpf = :cpf”)

O EntityManager utiliza o nome da consulta para instanciá-la, o que é feito através do método createNamedQuery(). Depois que a consulta é criada, basta setar os parâmetros e executá-la. A execução pode ser feita pelos métodos getSingleResult() ou getResultList(), a depender do resultado esperado. Por exemplo, para localizar uma pessoa pelo CPF (supondo que o retorno será único) basta executar a consulta conforme o exemplo abaixo:
 
Query consulta = manager.createNamedQuery(“consultarPorCPF”);
consulta.setParameter(“cpf”, “111.111.111-11”);
Pessoa pessoa = consulta.getSingleResult();

As consultas dinâmicas não possuem nome, e podem ser construídas em tempo de execução. A criação desse tipo de consulta é feito através do método createQuery():

Query consulta = manager.createQuery(
      “SELECT p FROM Pessoa p WHERE p.cpf = :cpf”);

É interessante notar que a execução de ambas as consultas é idêntica, uma vez que as duas são objetos do tipo Query.

Um exemplo completo

Agora que os conceitos básicos da API foram apresentados, partiremos para um exemplo completo, onde a JPA será demonstrada na prática.

Enquanto a maioria dos artigos sobre JPA foca no uso de EJBs, aqui faremos uma abordagem distinta. No exemplo deste artigo implementaremos uma aplicação desktop sem nenhum vínculo com o container EJB. O objetivo é mostrar que a JPA pode ser utilizada no Java SE facilmente. Usaremos na camada de persistência o TopLink Essentials e o MySQL. O TopLink Essentials é a versão open source do TopLink, uma ferramenta de mapeamento objeto-relacional da Oracle. O TopLink Essentials implementa a JPA e é utilizado no projeto GlassFish, que é a implementação de referência do Java EE 5.0. Por simplicidade, e para focar melhor na API, não implementamos uma interface gráfica. O código completo da aplicação está disponível no site da Java Magazine. Veja mais sobre o ambiente utilizado, e como executar a aplicação de exemplo, no quadro “Configurando o ambiente”.

A aplicação implementa um sistema (bastante simplificado) para gerenciamento de uma locadora de veículos. Na Figura 1 é exibido seu diagrama de classes e na Figura 2 o modelo ER (Entidade-Relacionamento) utilizado

...

Quer ler esse conteúdo completo? Tenha acesso completo