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

img

Clique aqui para ler esse artigo em PDF.imagem_pdf.jpg

Parte 1: Requisitos, modelo de classes e persistência com JPA

Comece a criar uma aplicação Java EE 5 completa, usando as mais comentadas tecnologias de desenvolvimento Java

Vamos dar início a uma série de artigos, que apresentará a construção de uma aplicação completa, utilizando algumas das mais comentadas e poderosas tecnologias para desenvolvimento de software Java: JPA, JSF e AJAX, e EJB 3.0.

O que nos chama a atenção nessas tecnologias é simplicidade e a produtividade oferecidas. É importante poder construir software a partir do zero, de forma rápida. Mas a produtividade só é conquistada plenamente quando não precisamos construir tudo do início a cada mudança de cenário. Não estamos falando de “copy-paste” de código. Queremos criar software reutilizável, e vamos fazê-lo extraindo o melhor desse fascinante conjunto de tecnologias.

Para ilustrar nosso estudo, vamos definir e atender um conjunto de requisitos de software que permitirá explorar essas tecnologias de forma prática. Mas não ficaremos apenas na apresentação técnica. O estudo de caso nos levará a várias reflexões do ponto de vista de um projetista de software. Quando adotar essas tecnologias? Como combiná-las? Como desenvolver componentes reutilizáveis em múltiplas arquiteturas Java EE e em extensões do estudo de caso original? Que design patterns são adequados para utilizar com essas tecnologias?

Neste primeiro artigo, vamos apresentar e interpretar o conjunto de requisitos do nosso estudo de caso, e começaremos projetando e desenvolvendo as classes persistentes com a tecnologia JPA. Faremos também uma implementação simplificada dos componentes de negócio. Você encontrará no site da Java Magazine todo código fonte desta parte, e um script Ant para construção de um ambiente de testes.

Requisitos e classes de negócio

Na Figura 1 apresentamos o diagrama de classes conceitual do exemplo, na forma como seria desenvolvido por um analista de negócios. Esse diagrama descreve as classes envolvidas no processo de abertura de matrículas em um centro de treinamento. A classe Matricula é uma classe associativa que vincula o Aluno a um Treinamento.  Os requisitos a seguir também foram levantados pelo analista:

Requisitos Funcionais

1.     Os cadastros de alunos e turmas devem ser mantidos em um módulo administrativo. As matrículas serão criadas e canceladas pelo módulo de controle de matrículas.

2.     Uma turma deve conter o código do curso e um número seqüencial gerado em função do curso, e só pode ser alterada através do módulo administrativo. Além disso, a data de início de uma turma só pode ser estabelecida com pelo menos 24 horas de antecedência.

3.     Os dados cadastrais de um aluno podem ser atualizados durante a criação de uma matrícula. Um aluno não pode se matricular mais de uma vez em uma turma.

4.     Uma turma não pode comportar matrículas acima de sua quantidade de vagas.

Requisitos Não-funcionais

5.     A aplicação deve apresentar uma interface web

6.     A aplicação deve suportar múltiplos usuários simultâneos

7.     A aplicação deve ser flexível para utilizar bancos de dados relacionais diversos

8.     Inicialmente a aplicação deve ser implantada em um container web

9.     A aplicação deverá ser facilmente escalável, para que depois possa acomodar os componentes de negócio em um container à parte.

10. No futuro, a aplicação poderá ser fornecida, como um framework, a diversas instituições educacionais, que podem ou não utilizar um container de componentes de negócio em sua infra-estrutura.

Um container de componentes de negócio oferece infra-estrutura robusta para hospedar os objetos que contêm as regras negócio de uma aplicação corporativa. Na especificação Java EE, trata-se de um container EJB.

img

Figura 1. Diagrama de classes conceitual

Decisões de projeto

Interpretando os requisitos não-funcionais podemos optar tranqüilamente pela tecnologia Java Enterprise Edition 5.

Numa abordagem “bottom-up” (de baixo para cima), começamos a decidir pelo repositório de dados, em direção à interface gráfica.

Banco de dados relacional

Para o ambiente de desenvolvimento vamos utilizar o banco de dados relacional HSQLDB (escrito em Java), por ser leve e fácil de instalar. Isso sem perder de vista que a aplicação poderá trabalhar com outros bancos de dados (requisito 7).

Persistência Objeto-Relacional

Qual usar dentre as principais alternativas para a camada de persistência? JDBC, Entity Beans (EJB 2.x), ou JPA? JDBC pode ser trabalhoso se considerarmos a independência do BD requerida. Entity Beans, que só funcionam dentro de containers EJB, estão descartados pela 8 exigência de a primeira versão rodar num container web.

Já a JPA (Java Persistence API) é parte de um padrão formal (EJB 3.0), que e é capaz de atender aos requisitos 7, 8 e 9. Além disso, a JPA é necessariamente suportada por containers EJB 3.0. A adoção de JPA vai facilitar muito a reutilização de software discutida no início. Veja mais sobre essa tecnologia no quadro “Java Persistence API” (e veja uma introdução à JPA na Edição 39).

A escolha por JPA implica também na escolha de um provedor JPA. Por comodidade, vamos utilizar o mesmo provedor JPA disponibilizado no container Java EE escolhido.

Container de componentes de negócio

Containers EJB 3.0 podem ser escolhidos para atendermos ao requisito 9 (escalabilidade). O JBoss 4.0.5 será escolhido. Outras boas escolhas, dentre os containers gratuitos/livres, seriam o Glassfish, o Sun Application Server ou o JBoss 5 beta. O JBoss utiliza o Hibernate Entity Manager como provedor JPA default, portanto vamos usar esse provedor.

·        Componentes de negócio: Como o requisito de escalabilidade exige alguma flexibilidade, devemos adotar uma abordagem híbrida e flexível onde as classes de negócio podem facilmente ser convertidas para EJBs. Por enquanto vamos desenvolver componentes de negócio através de classes Java comuns (POJOs), e no final da série entraremos no mérito da utilização de componentes EJB.

·        Container web: O Tomcat é uma boa escolha inicial por ser gratuito, e ser a implementação de referência de containers web, além de amplamente conhecido. .

·        Componentes web: Podemos utilizar os componentes web tradicionais da especificação Java EE: Servlets, JSPs e bibliotecas de tags customizadas. Ou devemos considerar frameworks tradicionais como o Struts, e também o JavaServer Faces? Deixaremos para o próximo artigo a discussão sobre a adoção de tecnologias web.

 

Verifique no quadro “Checklist” como obter e instalar os produtos que serão utilizados neste primeiro artigo da série, e como trabalhar com o script de testes.

Classes persistentes com JPA

A Figura 2 apresenta o diagrama de classes de implementação elaborado a partir do diagrama conceitual e dos requisitos.

Os requisitos 6 e 4, especificamente, expressam que o processo de criação de matrículas para uma mesma turma pode ser realizado concorrentemente, por múltiplos usuários da aplicação, e que devem ser tomados todos os cuidados para não sobrecarregar uma turma com matrículas além da quantidade de vagas estabelecidas. Por isso, verificamos a presença de um atributo adicional chamado versao, na classe Turma. Este atributo é importante para controlar a concorrência sobre os registros de turmas. A estratégia adotada para o controle de concorrência é discutida no quadro “Lock otimista”.

Note também a presença da classe TurmaId, que combina as informações necessárias para identificar uma turma (requisito 2)

img

Figura 2. Diagrama de classes de implementação

A Listagem 1 mostra a classe Aluno, que é a mais simples do modelo e será utilizada para introduzir os conceitos de JPA. A interface Serializable permitirá que os objetos sejam transmitidos entre os containers web e EJB, no futuro.

 

Listagem 1. Classe Persistente: Aluno.java

package jm.matriculas.model;

 

import java.io.Serializable;

import javax.persistence.*;

 

@Entity

@NamedQueries(value={

   @NamedQuery(name="alunoByCpf",

      query="select a from Aluno a where a.cpf = :cpf"),

   @NamedQuery(name="alunoByNome",

      query="select a from Aluno a where a.nome like :nome")

})

public class Aluno implements Serializable {

  @Id @GeneratedValue(strategy=GenerationType.IDENTITY)

  private Integer id;

 

  private String nome;

  private String cpf;

  private String email;

  private String telefone;

  // ...getters & setters

}

Anotações básicas de Mapeamento

Utilizamos algumas anotações (precedidas com @) do JPA (definidas no pacote javax.persistence). Essas anotações estabelecem correspondências entre a classe persistente e sua tabela no banco de dados relacional. É o que chamamos de mapeamento objeto-relacional. Existem vários defaults da JPA que agilizam o processo de mapeamento, e que estão sendo aproveitados aqui:

1.     Ao marcar a classe com @Entity, estamos definindo que se trata de uma classe persistente. Os alunos serão persistidos na tabela “aluno”, pois por default o provedor JPA considera o nome da classe igual ao nome da tabela. Quando a tabela tem um nome diferente, utilizamos @Entity @Table(name=”nome-da-tabela”).

2.     O Atributo id é precedido de duas anotações. @Id estabelece que este atributo corresponde ao identificador de banco de dados para o objeto. Por default o provedor JPA entende que existe na tabela uma coluna nomeada “id” (por conta do nome do atributo). A anotação @GeneratedValue configura que o valor do atributo identificador será gerado automaticamente, no momento em que novos objetos forem persistidos. Quando o identificador deve ser estabelecido e gerenciado manualmente pela aplicação, simplesmente omitimos @GeneratedValue. A configuração strategy=GenerationType.IDENTITY faz com que o JPA considere a coluna de banco de dados como auto-incrementável, e que sejam utilizados os valores gerados pelo banco de dados. Se o banco usasse “sequences” ao invés de colunas auto-incrementáveis, aplicaríamos em conjunto:

 

@Id @GeneratedValue(strategy=GenerationType.SEQUENCE)

  @SequenceGenerator(name=”nome-da-sequence”)

 

3.     Todos os atributos da classe são considerados persistentes e por default serão armazenados em colunas de mesmo nome. Quando um atributo deve ser mapeado para uma coluna de nome diferente utilizamos ...

Quer ler esse conteúdo completo? Tenha acesso completo