Bom, neste tópico iremos direto ao assunto. Meu objetivo é demonstrar a criação de um CRUD em poucos passos, utilizando Java com recursos de JPA, JSF na arquitetura MVC.
Por que construir um CRUD?
Em termos de desenvolvimento, um CRUD estabelece os procedimentos básicos operacionais para um componente de um sistema. Por componente, utilizando orientação a objetos, entendemos como um objeto pertencente ao modelo (um Cliente, um Produto, por exemplo). Por procedimentos básicos, entendemos como as operações de inclusão (create), seleção (read), atualização (update) e exclusão (destroy) associadas a um desses objetos. Todo sistema possui seus CRUDs. Ao mostrar um exemplo de criação de CRUD, temos base para a criação de várias etapas de um sistema.
Por que utilizar a linguagem Java?
Hoje, em grande maioria, quando se fala em sistemas, se fale em desenvolvimento web. Com a Web 2.0 o desenvolvimento de sistemas para Web se intensificou ao ponto de chegarmos a detalhes de produção e qualidade muito próximos aos sistemas cliente-servidor. Dentre as linguagens atuais, o Java se destaca pelo conjunto de Frameworks que oferecem um desenvolvimento sustentável, com muitas facilidades de manutenção. O .Net (com o C#) também é excelente para grandes projetos. O PHP também não pode ser esquecido, porém sua manutenção não é tão favorecida. Enfim, Java também é livre e aberto, dando um ponto forte para minha escolha.
Por que utilizar JPA?
O JPA, na versão 3 do EJB, é indiscutivelmente o Framework mais poderoso da atualidade (em minha singela opinião). Através das diversas possibilidades oferecidas pelo JPA, utilizaremos um pequeno, mas fantástico escopo de persistência. Em essência, trabalha-se com anotações JPA, integradas aos Beans. Essas características serão melhores discutidas nos exemplos apresentados.
Por que trabalhar com JSF?
O Java ServerFaces é uma das mais atuais alternativas para se construir a camada view da aplicação web. Ela permite ao desenvolvedor utilizar tags específicas, sem nenhuma codificação nativa em Java (scriptles) no arquivo jsp. As páginas dinâmicas ficam ligadas diretamente aos Beans. A produtividade é favorecida e a manutenção facilitada, como veremos.
Por que utilizar a arquitetura MVC?
A arquitetura MVC (também estendida para MVCP, sendo o P de persistence) é amplamente utilizada como padrão de desenvolvimento. O model (M) define o comportamento do sistema, implementando os Beans, também conhecidos como Pojo (Plain Old Java Object). O view (V) define a camada de visualização (onde utilizaremos o JSF). O controller (C) define as regras de negócio da aplicação – pode-se utilizar os Beans de Sessão fornecidos pelo EjB3. Não trabalharemos com esses tópicos neste estudo. Vamos utilizar uma camada isolada do modelo e da apresentação.
Por que utilizar o Netbeans?
Deve-se atentar para a seguinte questão: Netbeans, Eclipse, JBuilder, JCompany, entre outras, são excelentes IDEs de desenvolvimento para aplicações Java, mas a grande jogada não são as IDEs, e sim o Java. O que quero dizer é o seguinte: cada desenvolvedor trabalha com a IDE que mais se adaptar, ou o que for estabelecido em sua empresa. Todos são bons, porém, é possível chegar aos mesmos resultados em todos eles. Uns fornecem algumas facilidades, mas podemos produzir os mesmos resultados com qualquer um deles. Escolhi o Netbeans neste estudo por sua facilidade em relação ao Java. Não precisaremos instalar plugins. Além do fato dele ser livre. Portanto, não tome esse tutorial como um modelo de produção onipresente. Podemos produzir o mesmo resultado na unha – configurando os diretórios no container, bibliotecas e arquivos Java nos comandos do prompt, se a necessidade de ficarmos associados a nenhuma IDE.
Por que utilizar o PostgreSQL?
Todos os bancos livres são bons. Cada um com suas qualidades próprias. Fique à vontade para trabalhar com outros bancos. O fato de se trabalhar com o JPA facilita ainda mais a transição de um banco para o outro. Precisamos apenas alterar a JAR e o arquivo de configuração persistence.xml. O SQL apresentado poderá também ser facilmente adaptado, pois mantive uma certa flexibilidade no código.
Modelo a ser exemplificado
Abaixo temos o modelo que será abordado:
Faremos o sistema para Manter Telefones. A única classe do modelo será essa. Outras classes serão necessárias para compor a arquitetura. Ao final do desenvolvimento, teremos as classes de modelo (essa), as classes Data Access Object (para persistência), as classes Business Object (para as regras de negócio) e as classes View (Beans para controle da interface com usuário e sessão). Entre elas, serão necessárias algumas interfaces Java para padrões de projeto adequados ao exemplo. Para a criação deste diagrama, utilizei o Jude (http://jude.change-vision.com/jude-web/index.html), que além de facilitar a criação gráfica do modelo, gera código Java.
Obs. Desconsidere a modelagem apresentada, pois o ideal seria termos um objeto Cliente relacionado com Telefone, mas como este é um simples exemplo acadêmico, temos só Telefone.
Criação do banco de dados
Os recursos de persistência implementados pelo Toplink, oferecem um recurso de geração automática, porém, prefiro manter a implementação do banco manual. Desta forma, no futuro, quando mudarmos a linguagem, o paradigma, nosso banco estará íntegro. Em alguns casos, o modelo referencial gerado automaticamente não fica, digamos, totalmente relacional.
Abaixo temos o SQL de geração da tabela. O nome do banco é mycrud e a codificação é UTF8.
create table telefones (
id integer not null,
ddd char(2) not null,
numero char(8) not null,
nome varchar(40) not null,
constraint pk_telefones primary key (id)
);
Observe que, por opção, não criaremos um auto-incremento no banco. Isto será gerenciado pelo próprio Toplink. Portanto, será necessária a criação da tabela e inserção abaixo.
CREATE TABLE SEQUENCE (SEQ_NAME VARCHAR(50) NOT NULL, SEQ_COUNT DECIMAL(38), PRIMARY KEY (SEQ_NAME));
INSERT INTO SEQUENCE(SEQ_NAME, SEQ_COUNT) values ('SEQ_GEN', 1);
Essa tabela é utilizada para controlar o auto-incremento, independente do banco. Criada essas duas tabelas, poderemos partir para o desenvolvimento. Alguns detalhes importantes são: o usuário e senha do meu banco são, respectivamente, pgadmin e pgadmin – vamos precisar dessa informação para a criação do arquivopersistence.xml.
Utilizei a ferramenta phpPgAdmin para a criação do banco e geração das tabelas (http://phppgadmin.sourceforge.net/).
Criação do Projeto no Netbeans
Detalhes: Estamos trabalhando com a versão 6.1 do Netbeans, o container Tomcat 6.0.16 e o JEE 5.
Em primeiro lugar, recomendo que todos os outros projetos que estejam abertos no Netbeans sejam fechados – só por questão de organização. Abaixo temos os passos necessários para a criação do projeto.
- Clique em Arquivo, Novo Projeto...
- Na janela Novo Projeto, dentro da aba Categorias, selecione a opção Web. Na aba Projetos, selecione a opção Aplicação Web
- Clique em Próximo >
- Em Nome do projeto, defina o valor mycrud
- Clique em Próximo >
- Em Servidor, selecione a opção Apache Tomcat 6.0.16
- Clique em Próximo >
- Na aba Frameworks, marque apenas a opção JavaServer Faces
- Clique em Finalizar
Neste ponto, teremos nosso projeto Web criado. Vamos acrescentar a opção de persistência de dados JPA com a sequência abaixo.
- Na aba Projetos (à esquerda da interface do Netbeans), clique com o botão direito no nome do projeto (mycrud), selecione a opção Novo, e o submenuOutro...
- Na aba Categorias, selecione Persistence
- Na aba Tipos de arquivos selecione a opção Unidade de persistência
- Clique em Próximo >
- Defina o valor de Nome da unidade de persistência como default
- Em Conexão de banco de dados, selecione a opção Nova conexão com banco de dados...
- Em Nome, selecione a opção PostgreSQL
- Em URL do banco de dados, defina o valor jdbc:postgresql://localhost:5432/mycrud
- Em Nome do usuário e Senha, defina respectivamente, pgadmin e pgadmin. Lembre-se que meu banco está configurado com esse usuário. Faça suas adaptações, se necessário.
- Marque a opção Lembrar senha
- Clique em OK
- Em Selecione um esquema, selecione public
- Clique em OK
- Em Estratégia de geração de tabela, marque a opção Nenhum
- Clique em Finalizar
Neste momento, o arquivo persistence.xml será criado. Por enquanto, podemos fechá-lo. Feche também o arquivo welcomeJSF.jsp. Posteriormente vamos excluí-lo.
Vamos agora adicionar a biblioteca de conexão com o PostgreSQL.
- Na aba Projetos, clique com o botão direito em Bibliotecas
- Selecione a opção Adicionar biblioteca...
- Selecione a opção PostgreSQL JDBC Driver
- Clique em Adicionar biblioteca
Vamos agora criar os pacotes Java.
- Na aba Projetos, clique com o botão direito em Pacotes de códigos-fonte
- Clique na opção Novo, e no submenu Outro...
- Na aba Categorias, selecione a opção Java
- Na aba Tipos de arquivos, selecione a opção Pacote Java
- Clique em Próximo >
- Em Nome do pacote, defina o valor model
- Clique em Finalizar
- Repita a sequência de 1 ao 5
- Em Nome do pacote, defina o valor persistence
- Clique em Finalizar
- Repita a sequência de 1 ao 5
- Em Nome do pacote, defina o valor controller
- Clique em Finalizar
- Repita a sequência de 1 ao 5
- Em Nome do pacote, defina o valor view
- Clique em Finalizar
- Repita a sequência de 1 ao 5
- Em Nome do pacote, defina o valor util
- Clique em Finalizar
Observe que criamos cada uma das camadas do padrão MVCP – model, view, controller e persistence. Neste ponto, estamos prontos para começar a codificação Java (enfim...).
Criação do Modelo (camada Model)
O primeiro passo é criar uma Inteface que será implementada em todos os Pojos. Essa interface, além de garantir a correta utilização dos objetos nas próximas camadas, seria usado na geração de componentes ComboBox. Neste projeto não criaremos nenhum, mas através dessa Interface isso seria possível e facilitado (quem sabe em outros futuros artigos veremos tais detalhes...).
- Na aba Projetos, clique com o botão direito em Pacotes de códigos-fonte
- Clique na opção Novo, e no submenu Outro...
- Na aba Categorias, selecione a opção Java
- Na aba Tipos de arquivos, selecione a opção Interface Java
- Clique em Próximo >
- Em Nome da classe, digite InterfacePojo
- Em Pacote, selecione a opção model
- Clique em Finalizar
Substitua todo o conteúdo do arquivo pelo código abaixo:
// InterfacePojo.java
package model;
public interface InterfacePojo {
public int getCode();
public void setCode(int code);
public String getDescribe();
public void setDescribe(String describe);
}
Lembre-se sempre de salvar as alterações (menu Arquivo, opção Salvar todos). Vamos agora criar o Pojo (model) do objeto Telefone.
- Na aba Projetos, clique com o botão direito em Pacotes de códigos-fonte
- Clique na opção Novo, e no submenu Outro...
- Na aba Categorias, selecione a opção Java
- Na aba Tipos de arquivos, selecione a opção Classe Java
- Clique em Próximo >
- Em Nome da classe, digite Telefone
- Em Pacote, selecione a opção model
- Clique em Finalizar
Substitua o conteúdo do arquivo pelo código abaixo:
// Telefone.java
package model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
@Entity(name = "Telefone")
@Table(name = "telefones")
@NamedQueries( {
@NamedQuery(
name="selectTelefone",
query = "select obj from Telefone obj"
)
}
)
public class Telefone implements Serializable, InterfacePojo {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
@Column(name = "id")
private int id;
@Column(name = "ddd", nullable = false, length = 2)
private String ddd;
@Column(name = "numero", nullable = false, length = 8)
private String numero;
@Column(name = "nome", nullable = false, length = 40)
private String nome;
public void setId(int id) { this.id = id; }
public int getId() { return id; }
public String getDdd() { return ddd; }
public String getNome() { return nome; }
public String getNumero() { return numero; }
public void setDdd(String ddd) { this.ddd = ddd; }
public void setNome(String nome) { this.nome = nome; }
public void setNumero(String numero) { this.numero = numero; }
// Métodos da InterfacePojo
@Override
public int getCode() { return this.getId(); }
@Override
public void setCode(int code) { this.setId( code ); }
@Override
public String getDescribe() { return this.getNome(); }
@Override
public void setDescribe(String describe) {
this.setNome( describe );
}
// Métodos da InterfacePojo
// Métodos de formatação
public String getNumeroFormatado() {
return "(" + this.getDdd() + ") " + this.getNumero();
}
// Métodos de formatação
}
Neste código, devemos nos atentar nas anotações EJB 3:
@Entity – usado para definir a entidade
@Table – usado para definir o nome da tabela no banco de dados
@NamedQueries e @NamedQuery – são usadas para definir a linguagem JPQL (Java Persistence Query Language) de acesso aos dados do banco. Observe que é muito similar à SQL, porém, aqui se trabalha com Orientação a Objetos.
@Id – Usado para definir que o atributo em questão, será a chave primária na tabela
@GeneratedValue – Usado para definir automaticamente o valor do campo em questão
@Column – Usado para definir o nome do campo da tabela no qual este atributo está relacionado. Observe que existem as diretivas length e nullable que servem, respectivamente, para definir o tamanho máximo do campo e se o mesmo pode ser nulo.
@Override – esta anotação é usada para mostrar ao compilador que este método deverá ser utilizado ao invés do método da Interface. Neste caso, é obvio que estes métodos serão usados (pois somos obrigados a implementá-los por conta da InterfacePojo), mas mesmo assim, é boa prática de programação colocar o @Override nos casos em que um método é sobrescrito (polimorfismo).
Através destes códigos, o Objeto poderá ser manipulado no banco pelo Toplink. O caso do @Override é indiferente para o serviço de persistência. Observe também que, por se tratar de um Pojo, simples getters e setters são suficientes (e por padrão, necessários) para o código. O modelo é definido com classes neste design.
O fato de o objeto ser Serializable e possuir o atributo serialVersionUID permite sua migração entre Container's, com a aplicação de JNDI. Não trabalharemos com esse recurso neste material.
Salve as alterações.
Vamos agora alterar o arquivo de persistência para que o objeto Telefone possa ser manipulado.
- Na aba Projetos, no item Arquivos de configuração
- Dê um duplo clique em persistence.xml
- Insira o código <class>model.Telefone</class> entre as tags </provider> e <properties>
O código do arquivo ficará igual ao mostrado abaixo:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="default" transaction-type="RESOURCE_LOCAL">
<provider>oracle.toplink.essentials.PersistenceProvider</provider>
<class>model.Telefone</class>
<properties>
<property name="toplink.jdbc.user" value="pgadmin"/>
<property name="toplink.jdbc.password" value="pgadmin"/>
<property name="toplink.jdbc.url" value="jdbc:postgresql://localhost:5432/mycrud"/>
<property name="toplink.jdbc.driver" value="org.postgresql.Driver"/>
</properties>
</persistence-unit>
</persistence>
O código deste arquivo mostra ao Toplink como trabalhar com o banco de dados. Mostra também, quais classes poderão ser manipuladas. Neste caso, só a classe Telefone.
Salve as alterações e vamos prosseguir para a camada de persistência.
Criação da Persistência (camada Persistence)
A camada de persistência consiste em um Data Access Object (DAO) geral que implementa os procedimentos do CRUD (inserção, alteração, exclusão e seleção). Ele será responsável pela captura das Exceptions de persistência que, quando encontradas, serão jogadas para a camada acima através de uma PersistenceException. Em geral, iremos passar um objeto para os métodos dessa camada e ele executará (através dos recursos do EJB 3) todos os procedimentos sozinho.
Deve-se também atentar para a necessidade de criar um Singleton do DAO.
O que é um Singleton?
Trata-se de uma classe com métodos estáticos criados para manter um único objeto DAO para todas as classes que precisarem dele. Observando o código da classe, será mais fácil entender a filosofia. Vamos criar o arquivo DAO.
- Na aba Projetos, clique com o botão direito em Pacotes de códigos-fonte
- Clique na opção Novo, e no submenu Outro...
- Na aba Categorias, selecione a opção Java
- Na aba Tipos de arquivos, selecione a opção Classe Java
- Clique em Próximo >
- Em Nome da classe, digite Dao
- Em Pacote, selecione a opção persistence
- Clique em Finalizar
Substitua o conteúdo do arquivo pelo código abaixo.
// Dao.java
package persistence;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.PersistenceException;
import javax.persistence.PersistenceUnit;
import javax.persistence.Query;
@PersistenceUnit
public class Dao {
private static EntityManagerFactory emf =
Persistence.createEntityManagerFactory("default");
private static EntityManager em = emf.createEntityManager();
private static Dao dao;
public Dao() {}
public static Dao getDataAccessObject() {
if ( dao == null ) dao = new Dao();
return dao;
}
public void create(Object object) throws PersistenceException {
try {
em.getTransaction().begin();
em.persist( object );
em.getTransaction().commit();
em.clear();
} catch (Exception e) {
throw new PersistenceException( "Não foi possível criar o objeto!" );
}
}
public List<?> read(String namedQuery) throws PersistenceException {
try {
Query query = em.createNamedQuery( namedQuery );
List<?> collection = query.getResultList();
em.clear();
return collection;
} catch (Exception e) {
throw new PersistenceException( "Não foi possível realizar a consulta!" );
}
}
public List<?> executeQuery(String ejbQL, String parameter[])
throws PersistenceException {
try {
Query query = em.createQuery( ejbQL );
if ( parameter != null ) {
for (int i = 0; i < parameter.length; i++)
query.setParameter( (i+1) , parameter[ i ] );
}
List<?> collection = query.getResultList();
em.clear();
return collection;
} catch (Exception e) {
throw new PersistenceException( "Não foi possível realizar a consulta!" );
}
}
public void update(Object object) throws PersistenceException {
try {
em.getTransaction().begin();
em.merge( object );
em.getTransaction().commit();
em.clear();
} catch (Exception e) {
throw new PersistenceException( "Não foi possível alterar o objeto!" );
}
}
public void destroy(Object object) throws PersistenceException {
try {
em.getTransaction().begin();
em.remove( em.merge( object ) );
em.getTransaction().commit();
em.clear();
} catch (Exception e) {
throw new PersistenceException( "Não foi possível excluir o objeto!" );
}
}
@SuppressWarnings("unchecked")
public Object searchObjectByCode(Class objectClass, Object objectId)
throws PersistenceException {
try {
Object object = em.find( objectClass , objectId );
em.clear();
return object;
} catch (Exception e) {
throw new PersistenceException( "Não foi possível obter o objeto!" );
}
}
}
A funcionalidade Singleton foi introduzida através do método getDataAccessObject(). O objeto Dao será criado nas outras camadas através deste método, que retorna uma nova referência ou a referência já utilizada por outra sessão. Observe que os Exceptions gerados pelo banco não serão detalhadamente filtrados, devido à simplicidade mostrada neste exemplo. Em todos os casos, somente uma mensagem será liberada. Observe também que foi criado um método chamado executeQuery(), utilizado para executar namedQuery com parâmetros. Esse recurso será muito útil para validações e regras realizadas no Bo.
Criação das classes de apoio (pacote util)
Algumas classes serão criadas para apoiar o funcionamento geral do sistema. A primeira delas será a MyCrudException utilizada como exceção para as regras de negócio.
- Na aba Projetos, clique com o botão direito em Pacotes de códigos-fonte
- Clique na opção Novo, e no submenu Outro...
- Na aba Categorias, selecione a opção Java
- Na aba Tipos de arquivos, selecione a opção Classe Java
- Clique em Próximo >
- Em Nome da classe, digite MyCrudException
- Em Pacote, selecione a opção util
- Clique em Finalizar
O código desta classe, muito simples, aliás, é mostrado abaixo.
// MyCrudException.java
package util;
public class MyCrudException extends Exception {
public MyCrudException() { super(); }
public MyCrudException(String message) { super( message ); }
}
O próximo objeto é responsável pela criação de um componente de validação de números. Você pôde observar que nosso Pojo telefone possui campos ddd e número do tipo String. Mas na realidade estes valores são apenas numéricos. Neste objeto faremos tal verificação. Optei em não criar um método estático que seria usado. Isso depende da preferência do desenvolvedor. Para a criação deste novo arquivo, realize os procedimentos abaixo:
- Na aba Projetos, clique com o botão direito em Pacotes de códigos-fonte
- Clique na opção Novo, e no submenu Outro...
- Na aba Categorias, selecione a opção Java
- Na aba Tipos de arquivos, selecione a opção Classe Java
- Clique em Próximo >
- Em Nome da classe, digite Number
- Em Pacote, selecione a opção util
- Clique em Finalizar
Abaixo temos o código desta classe:
// Number.java
package util;
public class Number {
private int length;
private String value;
public Number() { this( 1 ); }
public Number(int length) { this( length , "0" ); }
public Number(int length, String value) {
this.setLength(length);
this.setValue(value);
}
public void setLength( int length ) {
this.length = ( length > 0 ) ? length : 1;
}
public void setValue(String value) {
this.value = value;
}
public int getLength() { return this.length; }
public String getValue() { return this.value; }
public boolean validate() {
if ( this.value == null ) return false;
if ( this.value.length() != this.length ) return false;
for ( int i = 0; i < this.length; i++ ) {
if ( "0123456789".indexOf( this.value.charAt( i ) ) == -1 )
return false;
}
return true;
}
}
Este objeto faz a verificação necessária para nosso sistema. Essas duas classes compõem no pacote util. Agora vamos para a implementação mais importante do projeto: as regras do negócio.
Criação das Regras de Negócio (camada Controller)
A filosofia será similar ao da camada de persistência. Haverá uma classe geral chamada Bo. Aqui, porém, precisaremos de uma classe de BO específica para o Telefone – onde serão criadas as regras de negócio. Haverá também uma Interface que definirá os procedimentos padrões de camada de Business Object. Abaixo temos o procedimento para a criação das classes e interfaces.
- Na aba Projetos, clique com o botão direito em Pacotes de códigos-fonte
- Clique na opção Novo, e no submenu Outro...
- Na aba Categorias, selecione a opção Java
- Na aba Tipos de arquivos, selecione a opção Interface Java
- Clique em Próximo >
- Em Nome da classe, digite InterfaceBo
- Em Pacote, selecione a opção controller
- Clique em Finalizar
Substitua o arquivo pelo código abaixo:
// InterfaceBo.java
package controller;
import java.util.List;
import model.InterfacePojo;
import util.MyCrudException;
public interface InterfaceBo {
public void create(InterfacePojo pojo) throws MyCrudException;
public List<?> read() throws MyCrudException;
public List<?> executeQuery(String ejbQL, String parameter[])
throws MyCrudException;
public void update(InterfacePojo pojo) throws MyCrudException;
public void destroy(InterfacePojo pojo) throws MyCrudException;
public Object searchObjectByCode(Class objectClass, Object id)
throws MyCrudException;
public void validate(InterfacePojo pojo) throws MyCrudException;
}
Agora será implementada a classe BO geral. Execute o procedimento abaixo:
- Após expandir o item Pacotes de códigos-fonte da aba Projetos, clique com o botão direito sobre o pacote controller
- Selecione a opção Novo e o submenu Classe java...
- Em Nome da classe digite Bo
- Clique em Finalizar
Substitua o conteúdo do arquivo Bo pelo código abaixo:
// Bo.java
package controller;
import java.util.List;
import javax.persistence.PersistenceException;
import model.InterfacePojo;
import persistence.Dao;
import util.MyCrudException;
public class Bo implements InterfaceBo {
private String namedQuery;
public Bo(String namedQuery) {
this.namedQuery = namedQuery;
}
@Override
public void create(InterfacePojo pojo) throws MyCrudException {
try {
validate( pojo );
Dao.getDataAccessObject().create( pojo );
} catch (PersistenceException e) {
throw new MyCrudException( e.getMessage() );
}
}
@Override
public List<?> read() throws MyCrudException {
try {
return Dao.getDataAccessObject().read( this.namedQuery );
} catch (PersistenceException e) {
throw new MyCrudException( e.getMessage() );
}
}
@Override
public List<?> executeQuery(String ejbQL, String parameter[])
throws MyCrudException {
try {
return Dao.getDataAccessObject().executeQuery(ejbQL, parameter);
} catch (Exception e) {
throw new MyCrudException( e.getMessage() );
}
}
@Override
public void update(InterfacePojo pojo) throws MyCrudException {
try {
validate( pojo );
Dao.getDataAccessObject().update( pojo );
} catch (PersistenceException e) {
throw new MyCrudException( e.getMessage() );
}
}
@Override
public void destroy(InterfacePojo pojo) throws MyCrudException {
try {
Dao.getDataAccessObject().destroy( pojo );
} catch (PersistenceException e) {
throw new MyCrudException( e.getMessage() );
}
}
@Override
public Object searchObjectByCode(Class objectClass, Object id)
throws MyCrudException {
try {
return Dao.getDataAccessObject().searchObjectByCode( objectClass , id );
} catch (PersistenceException e) {
throw new MyCrudException( e.getMessage() );
}
}
@Override
public void validate(InterfacePojo pojo) throws MyCrudException {}
}
Este código implementa a captura das Exceptions geradas pelo DAO, além de implementar um método de validação (ainda sem qualquer procedimento). O próximo objeto a ser implementado será o objeto mais importante do código, pois será através dele que as regras de negócio serão implementadas. Se você observou o código até o momento, pode reparar que foram utilizadas poucas condições (if). Dentro do possível controlamos as regras de desenvolvimento com opções de orientação a objetos. A grande massa de condições estruturadas ocorrerá no código de regras de negócio. A princípio, vamos implementar apenas os códigos básicos de validação – campos em branco ou com valor não compatível com o banco. Vamos colocar outras regras de negócio no final deste estudo, para mostrar simulações mais práticas.
- Após expandir o item Pacotes de códigos-fonte da aba Projetos, clique com o botão direito sobre o pacote controller
- Selecione a opção Novo e o submenu Classe java...
- Em Nome da classe digite TelefoneBo
- Clique em Finalizar
Substitua a implementação default do Netbeans do arquivo criado pelo código abaixo:
// TelefoneBo.java
package controller;
import model.InterfacePojo;
import model.Telefone;
import util.Number;
import util.MyCrudException;
public class TelefoneBo extends Bo {
public TelefoneBo() {
super( "selectTelefone" );
}
@Override
public void validate(InterfacePojo pojo) throws MyCrudException {
Telefone telefone = (Telefone) pojo;
// Validação do DDD
Number ddd = new Number( 2 , telefone.getDdd() );
if ( !ddd.validate() )
throw new MyCrudException( "Valor do DDD inválido!" );
// Validação do DDD
// Validação do Número
Number numero = new Number( 8 , telefone.getNumero() );
if ( !numero.validate() )
throw new MyCrudException( "Valor do Número inválido!" );
// Validação do Número
// Validação do Nome
if ( telefone.getNome() == null )
throw new MyCrudException( "Valor do Nome inválido!" );
if (
( telefone.getNome().length() < 1 ) ||
( telefone.getNome().length() > 40 )
) throw new MyCrudException( "Valor do Nome inválido!" );
// Validação do Nome
}
}
Vamos observar alguns fatores: como você observou, a maior parte da programação ocorre no objeto Bo. Essas funções serão herdadas pelo TelefoneBo, sendo que, como sobrescrevemos o método validate (e incluímos o @Override), quando um TelefoneBo estiver no Run Time, o método sobrescrito será utilizado no lugar do escrito no Bo. Isso significa que, quando for utilizada o método create (por exemplo), sua chamada à validate fará referência ao método de TelefoneBo. No final deste projeto, mostraremos outras validações. Agora entraremos na última camada.
Criação da interface com Usuário (camada View)
A camada View terá alguns detalhes a serem descritos. Durante a apresentação das classes e dos JSP's, falaremos mais sobre o desenvolvimento.
O primeiro passo será implementar o objeto ViewManager, responsável pelo gerenciamento da tela. Ele simplesmente controlará o desenvolvimento dos processos realizados pelo usuário.
- Após expandir o item Pacotes de códigos-fonte da aba Projetos, clique com o botão direito sobre o pacote view
- Selecione a opção Novo e o submenu Classe java...
- Em Nome da classe digite ViewManager
- Clique em Finalizar
Substitua o código pelo fragmento abaixo:
// ViewManager.java
package view;
import javax.faces.application.FacesMessage;
import javax.faces.application.FacesMessage.Severity;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import model.InterfacePojo;
public abstract class ViewManager {
private boolean formView;
private boolean formCreate;
private boolean formRead;
private boolean formUpdate;
private boolean formDestroy;
private String titleForm;
public ViewManager(String titleForm) {
this.titleForm = ( titleForm == null ) ? "CRUD" : titleForm;
this.clearForm();
this.formRead = true;
}
public String getTitleForm() {
if ( this.formView ) return titleForm + " - Visualizar";
if ( this.formCreate ) return titleForm + " - Inserir";
if ( this.formRead ) return titleForm + " - Consultar";
if ( this.formUpdate ) return titleForm + " - Atualizar";
if ( this.formDestroy ) return titleForm + " - Excluir";
return titleForm;
}
public boolean isFormView() { return this.formView; }
public boolean isFormCreate() { return this.formCreate; }
public boolean isFormRead() { return this.formRead; }
public boolean isFormUpdate() { return this.formUpdate; }
public boolean isFormDestroy() { return this.formDestroy; }
private void clearForm() {
this.formView = false;
this.formCreate = false;
this.formRead = false;
this.formUpdate = false;
this.formDestroy = false;
}
public void setFormView(ActionEvent event) {
this.clearForm();
this.formView = true;
setPojo( getObjectBySession() );
}
public void setFormCreate(ActionEvent event) {
this.clearForm();
this.formCreate = true;
createNewPojo();
}
public void setFormRead(ActionEvent event) {
this.clearForm();
this.formRead = true;
}
public void setFormUpdate(ActionEvent event) {
this.clearForm();
this.formUpdate = true;
setPojo( getObjectBySession() );
}
public void setFormDestroy(ActionEvent event) {
this.clearForm();
this.formDestroy = true;
setPojo( getObjectBySession() );
}
protected void showMessage(Severity severity, String resume, String detail) {
FacesContext.getCurrentInstance().addMessage
( null , new FacesMessage( severity , resume , detail ) );
}
public void createNewPojo() {}
public InterfacePojo getObjectBySession() { return null; }
public void setPojo(InterfacePojo pojo) {}
}
Este código contém os procedimentos que indicam quais partes da página JSP será exibida para os usuários. Por exemplo, a página de leitura dos dados (consulta) mostrará uma tabela (DataTable) contendo os dados e esta só será exibida quando isFormRead retornar true (o que ocorre, neste caso). Agora vamos construir o objeto de controle do objeto POJO. Ela também possui alguns métodos que precisam ser sobrescritos (createNewPojo(), getObjectBySession() e setPojo()). Vamos observá-los adiante.
- Após expandir o item Pacotes de códigos-fonte da aba Projetos, clique com o botão direito sobre o pacote view
- Selecione a opção Novo e o submenu Classe java...
- Em Nome da classe digite PojoManager
- Clique em Finalizar
Substitua o código pelo fragmento abaixo:
// PojoManager.java
package view;
import controller.Bo;
import java.util.List;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.servlet.ServletRequest;
import model.InterfacePojo;
import util.MyCrudException;
public abstract class PojoManager extends ViewManager {
private Bo bo;
private InterfacePojo pojo;
public PojoManager(String titleForm, Bo bo, InterfacePojo pojo) {
super(titleForm);
this.bo = bo;
this.pojo = pojo;
}
@Override
public void setPojo(InterfacePojo pojo) {
this.pojo = pojo;
}
public InterfacePojo getPojo() {
return this.pojo;
}
public void setBo(Bo bo) {
this.bo = bo;
}
public Bo getBo() {
return this.bo;
}
public void create(ActionEvent event) {
try {
this.bo.create(this.pojo);
super.setFormCreate(event);
super.showMessage(FacesMessage.SEVERITY_INFO,
null, "Objeto inserido com sucesso!");
} catch (MyCrudException e) {
super.showMessage(FacesMessage.SEVERITY_WARN,
null, e.getMessage());
}
}
public void destroy(ActionEvent event) {
try {
this.bo.destroy(this.pojo);
super.setFormRead(event);
super.showMessage(FacesMessage.SEVERITY_INFO,
null, "Objeto excluído com sucesso!");
} catch (MyCrudException e) {
super.showMessage(FacesMessage.SEVERITY_WARN,
null, e.getMessage());
}
}
public void update(ActionEvent event) {
try {
this.bo.update(this.pojo);
super.setFormRead(event);
super.showMessage(FacesMessage.SEVERITY_INFO,
null, "Objeto alterado com sucesso!");
} catch (MyCrudException e) {
super.showMessage(FacesMessage.SEVERITY_WARN,
null, e.getMessage());
}
}
public List<?> getList() {
List list = null;
try {
list = this.bo.read();
} catch (MyCrudException e) {
super.showMessage(FacesMessage.SEVERITY_WARN,
null, e.getMessage());
} finally {
return list;
}
}
@Override
public InterfacePojo getObjectBySession() {
InterfacePojo value = null;
try {
value = (InterfacePojo) ((ServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest()).getAttribute("pojo");
} catch (IllegalStateException e) {
this.showMessage(FacesMessage.SEVERITY_WARN,
null, "Não foi possível obter o objeto!" );
} finally {
return value;
}
}
public Object searchObjectByCode() {
Object value = null;
try {
value = this.bo.searchObjectByCode(
this.pojo.getClass(),
new Integer(this.pojo.getCode()));
if (value == null) {
super.showMessage(FacesMessage.SEVERITY_WARN,
null, "Objeto não encontrado!");
}
} catch (MyCrudException e) {
super.showMessage(FacesMessage.SEVERITY_WARN,
null, e.getMessage());
} finally {
return value;
}
}
}
Esta é, provavelmente, a classe mais complexa do projeto. Porém, depois de implementada, o ganho de produção para outros CRUDs é fantástico. Antes de tudo, vamos observar os principais comportamentos: através dos getters e setters de Pojo e Bo, poderemos gerenciar as respectivas classes. Outro comportamento importante é realizado pelos métodos comuns do CRUD (create, destroy, update e read), pois eles realizam o procedimento (através do Bo) e, caso ocorra, capturam as exceções geradas nas camadas inferiores. Através do showMessage (implementado no ViewManager.java) as mensagens serão enviadas para a tela (navegador). Outro método importante é definido em getObjectBySession(), que retorna o objeto selecionado na JSP. Quando isso ocorre? Na listagem de telefones, haverão campos de opções (links para visualizar, alterar e excluir) no qual o usuário poderá clicar. Neste momento, o objeto da linha selecionada (onde o link foi clicado), será enviado para o servidor. O código de getObjectBySession() obtém esse objeto (que, inclusive, possui o nome de pojo). Por padrão, não vamos implementar em PojoManager uma chamada ao método executeQuery() de Bo, pois caso haja necessidade de executar uma Query que não esteja especificada no Model, somente o Bo teria essa permissão (ou melhor, a princípio, somente ele deveria ter essa necessidade). Como você pode observar, as duas últimas classes são abstratas (não podem ser instanciadas). Isso deve ser assim pelo fato delas serem genéricas.
Vamos implementar agora, uma classe específica para Telefone que herdará todo o comportamento de PojoManager (e por consequência, ViewManager).
- Após expandir o item Pacotes de códigos-fonte da aba Projetos, clique com o botão direito sobre o pacote view
- Selecione a opção Novo e o submenu Classe java...
- Em Nome da classe digite TelefoneManager
- Clique em Finalizar
Substitua o contexto abaixo pelo código da classe criada:
// TelefoneManager.java
package view;
import controller.TelefoneBo;
import model.Telefone;
public class TelefoneManager extends PojoManager {
public TelefoneManager() {
super( "Telefone" , new TelefoneBo() , new Telefone() );
}
@Override
public void createNewPojo() {
Telefone telefone = new Telefone();
super.setPojo( telefone );
}
}
Como você pode observar, esta é uma simples classe que apenas sobrescreve o constructor – para definir as variáveis de instância para o ViewManager – e o createNewPojo() - utilizado para criar um novo pojo Telefone. Esta classe será nosso Bean de Sessão, porém, não será acessada diretamente. Criaremos uma classe chamada SessionBean que controlará todos os Beans da aplicação. Neste caso, como só temos um Bean, o TelefoneManager, que por sua vez, possui acesso direto ao Telefone, este será gerenciado pela SessionBean.
- Após expandir o item Pacotes de códigos-fonte da aba Projetos, clique com o botão direito sobre o pacote view
- Selecione a opção Novo e o submenu Classe java...
- Em Nome da classe digite SessionBean
- Clique em Finalizar
O código da classe é mostrado abaixo:
// SessionBean.java
package view;
public class SessionBean {
private TelefoneManager telefoneBean;
public SessionBean() {
this.telefoneBean = new TelefoneManager();
}
public TelefoneManager getTelefoneBean() {
return this.telefoneBean;
}
}
Para torná-la acessível pelos JSP's, precisamos adicioná-la ao faces-config.xml. Para trabalhar com esse arquivo, execute:
- Após expandir o item Arquivos de configuração da aba Projetos, dê dois cliques sobre o arquivo faces-config.xml
No início, será mostrada a IDE de organização dos JSP e navegação, porém, vamos trabalhar diretamente com o código. Clique (na parte superior do editor) na aba XML. O conteúdo do arquivo deverá ficar igual ao código mostrado abaixo:
<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="1.2"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd">
<managed-bean>
<managed-bean-name>SessionBean</managed-bean-name>
<managed-bean-class>view.SessionBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
</faces-config>
Observe que foi acrescentado uma diretiva <managed-bean> que indicará o arquivo java que será acessível pelo JSP. Esse parâmetro indica o seguinte:
· Item <managed-bean-name> : nome acessível pelas tags do JSP. Ou seja, se quisermos obter um atributo contido em SessionBean, o mesmo deverá (por padrão) ter o método getAtributo(). Se quisermos definir um valor para esse atributo, precisaremos do setAtributo(). Por exemplo, no código JSP, vamos acessar o atributo ddd do pojo telefone com a sintaxe:
SessionBean.telefoneBean.pojo.ddd
Ou seja, podemos referenciar SessionBean pois o mesmo está no faces-config.xml. SessionBean possui um atributo telefoneBean, que é acessível pelo getTelefoneBean(). Que por sua vez possui um método getPojo() - herdado do PojoManager – que retorna o Pojo no runtime – que se trata de um Telefone (pois TelefoneManager instancia o pojo Telefone em seu constructor). Por último, Telefone possui o método getDdd() que retorna o valor do atributo e setDdd() que define o valor do atributo. Esse acesso, por exemplo, poderia estar associado a um componente input do tipo text, indicando que o valor escrito no componente, será guardado (ou buscado) do Pojo Telefone. Veremos o código JSP adiante.
· Item <managed-bean-class> : indica – com o pacote – a classe associada ao Bean estabelecido.
· Item <managed-bean-scope> : indica o escopo deste Bean. Pode-se definir session, request ou application, cada um com sua utilidade. Se utilizarmos request, o bean tem seu ciclo de vida para uma única requisição, ou seja, ao “postarmos” uma página, o container instancia o Bean e realiza o procedimento. Em seguida o Bean é descartado. O session, mantém o Bean no container enquanto a sessão do usuário estiver ativa. Quando a sessão for finalizada, o container finaliza a vida dele (não do usuário - do bean, é claro). O escopo application dará ao Bean, tempo de vida durante toda a aplicação. Este é um caso muito particular, que precisa ser utilizado com cuidado, pois todos utilizaram de um mesmo recurso no sistema (o que se faz necessário, em alguns casos). Como nosso telefoneBean guardará vários estados da JSP, durante toda a sessão, vamos utilizar o escopo session.
Você deve estar se perguntando: porque não definir TelefoneManager diretamente no faces-config.xml ao invés de criar SessionBean. Bom, com uma única classe a ser gerenciada na sessão, realmente não precisamos de SessionBean. Porém, imagine que o sistema tenha dezenas de Beans, sendo todos com escopo de sessão. Para cada JSP que o usuário trabalhar, um Bean será instanciado no container. Ou seja, se o usuário acessar a tela de Telefones, o container instancia TelefoneManager. Se o usuário acessar a tela de Clientes (se existisse, por exemplo), o container instancia o Bean ClienteManager, só que, neste momento, não é mais necessário que TelefoneManager continue na memória. Que iria liberá-lo? Nosso SessionBean. Quando o usuário acessar uma nova página JSP, o SessionBean teria o cuidado de liberar da memória (com o código “telefoneBean = null;”, por exemplo) os Beans que ele não precisa mais. Atenção, o EJB 3 fornece Beans de sessão em seu framework, porém não veremos isso neste estudo (quem sabe futuramente?). Por fim, vamos criar a tela JSP.
Criação das telas JSP
Nesta etapa, finalmente criaremos a interface com usuário. Primeiramente, vamos criar dois templates através do recurso de tags (isso poderia ser feito, com algumas vantagens, com facelets). Vamos criar a pasta para guardar nossas tags. Por padrão, o diretório tags fica localizado dentro de WEB-INF. Execute o procedimento abaixo para criá-lo:
- Após expandir o item Páginas web, da aba Projetos, clique com o botão direito do mouse no diretório WEB-INF
- Selecione a opção Novo, item Outro...
- Na caixa Categorias, selecione Outro
- Na caixa Tipos de arquivos, selecione Diretório
- Clique em Próximo >
- Em Nome da pasta, entre com tags
- Clique em Finalizar
Todas as nossas tags serão guardadas aqui. Vamos criar a tag header.
- Na aba Projetos, abra o item Páginas web, em seguida, abra o diretório WEB-INF
- Clique com o botão direito no diretório tags, selecione a opção Novo, item Outro...
- Na caixa Categorias, selecione Web
- Na caixa Tipos de Arquivos, selecione Arquivo de marcação (Tag)
- Clique em Próximo >
- Em Nome do arquivo de marcação, defina o valor header
- Clique em Finalizar
Substitua o conteúdo do arquivo pelo código abaixo:
<%@tag description="put the tag description here" pageEncoding="UTF-8"%>
<table>
<tr>
<td width="20%" bgcolor="#898989"> </td>
<td align="center" bgcolor="#aeaeae">
<font class="title"> MyCRUD </font>
</td>
<td width="20%" bgcolor="#898989"> </td>
</tr>
</table>
Agora vamos criar a tag footer.
- Na aba Projetos, abra o item Páginas web, em seguida, abra o diretório WEB-INF
- Clique com o botão direito no diretório tags, selecione a opção Novo, item Outro...
- Na caixa Categorias, selecione Web
- Na caixa Tipos de Arquivos, selecione Arquivo de marcação (Tag)
- Clique em Próximo >
- Em Nome do arquivo de marcação, defina o valor footer
- Clique em Finalizar
Substitua o conteúdo do arquivo pelo código abaixo:
<%@tag description="put the tag description here" pageEncoding="UTF-8"%>
<table>
<tr>
<td bgcolor="#898989" height="3"></td>
</tr>
</table>
Agora, vamos criar nosso estilo CSS.
- Na aba Projetos, clique com o botão direito no item Páginas web
- Selecione a opção Novo, item Outro...
- Na caixa Categorias, selecione Outro
- Na caixa Tipos de arquivos, selecione Diretório
- Clique em Próximo >
- Em Nome da pasta, entre com css
- Clique em Finalizar
Nossa folha de estilo ficará aqui. Vamos criá-la:
- Na aba Projetos, abra o item Páginas web
- Clique com o botão direito no diretório css, selecione a opção Novo, item Outro...
- Na caixa Categorias, selecione Web
- Na caixa Tipos de Arquivos, selecione Folha de estilo em cascata
- Clique em Próximo >
- Em Nome do arquivo CSS, defina o valor style
- Clique em Finalizar
Substitua o conteúdo do arquivo pelo código abaixo:
/* style.css */
img.link {
border: 0px;
}
table {
border-collapse: collapse;
border: 0px;
padding: 0px;
background: white;
width: 500px;
}
table.empty {
border-collapse: collapse;
border: 0px;
padding: 0px;
width: 100%;
}
table.data {
border-collapse: collapse;
padding: 0px;
background-color: white;
width: 100%;
}
.form {
font: 12px Arial, sans-serif;
}
.footer {
text-align: right;
padding: 8px;
}
.header {
text-align: left;
font-weight: bold;
color: grey;
font: 14px Arial, sans-serif;
}
font.title {
font-weight: bold;
color: white;
font: 14px Arial, sans-serif;
}
.tableHeader {
color: black;
font: 12px Arial, sans-serif;
}
.tableData {
color: blue;
font: 12px Arial, sans-serif;
}
.severityInformation {
text-align: center;
font: 11px Arial, sans-serif;
font-weight: bold;
color: #2222aa;
}
.severityWarning {
text-align: center;
font: 11px Arial, sans-serif;
font-weight: bold;
color: #aaaa22;
}
.severityError {
text-align: center;
font: 11px Arial, sans-serif;
font-weight: bold;
color: #aa2222;
}
Por fim, vamos definir as imagens que utilizaremos no sistema.
- Na aba Projetos, clique com o botão direito no item Páginas web
- Selecione a opção Novo, item Outro...
- Na caixa Categorias, selecione Outro
- Na caixa Tipos de arquivos, selecione Diretório
- Clique em Próximo >
- Em Nome da pasta, entre com img
- Clique em Finalizar
Neste caso, vamos salvar as imagens diretamente no diretório onde está guardado o projeto. Caso você não saiba onde está, clique com o botão direito no projeto mycrud (na aba Projetos), selecione o menu Propriedades. No campo Pasta do projeto estará definido o caminho. Vamos supor que seu projeto esteja no path abaixo:
/home/guilherme/desenvolvimento/java/netbeans/mycrud
As imagens deverão ser salvas em:
/home/guilherme/desenvolvimento/java/netbeans/mycrud/web/img
Ou seja, exatamente no diretório que você criou. As imagens estão dispostas abaixo:
| ![]() | ![]() |
delete.gif
|
floppy.gif
|
lupa.gif
|
Após salva-las no diretório img, você poderá observá-las pelo Netbeans.
Vamos agora para a criação do nosso arquivo JSP. Primeiramente, vamos excluir o arquivo welcomeJSF.jsp. Para isso, execute:
- Abra o item Páginas web na aba Projetos
- Clique com o botão direito em welcomeJSF.jsp e selecione Excluir
- Na confirmação, clique em Sim
Vamos criar nosso JSP:
- Na aba Projetos, clique com o botão direito no item Páginas web
- Selecione a opção Novo, item Outro...
- Na caixa Categorias, selecione Web
- Na caixa Tipos de arquivos, selecione JSP
- Clique em Próximo >
- Em Nome do arquivo JSP, entre com crudTelefone
- Clique em Finalizar
Substitua o código gerado pelo conteúdo abaixo (que é grande, alias):
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib prefix="my" tagdir="/WEB-INF/tags" %>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="css/style.css">
<title>MyCRUD</title>
</head>
<body>
<my:header />
<table>
<tr>
<td>
<f:view>
<h:form id="editForm">
<h:dataTable styleClass="data"
footerClass="footer" headerClass="header"
value="#{SessionBean.telefoneBean.list}"
var="pojo" rendered="#{SessionBean.telefoneBean.formRead}">
<f:facet name="header">
<h:outputText value="#{SessionBean.telefoneBean.titleForm}" />
</f:facet>
<h:column>
<f:facet name="header">
<h:outputText value="Nome" styleClass="tableHeader"/>
</f:facet>
<h:outputText value="#{pojo.nome}" styleClass="tableData"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Telefone" styleClass="tableHeader"/>
</f:facet>
<h:outputText value="#{pojo.numeroFormatado}" styleClass="tableData"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Opções" styleClass="tableHeader"/>
</f:facet>
<h:commandLink actionListener="#{SessionBean.telefoneBean.setFormView}">
<h:graphicImage styleClass="link" value="img/lupa.gif" />
</h:commandLink>
<h:commandLink actionListener="#{SessionBean.telefoneBean.setFormUpdate}">
<h:graphicImage styleClass="link" value="img/floppy.gif" />
</h:commandLink>
<h:commandLink actionListener="#{SessionBean.telefoneBean.setFormDestroy}">
<h:graphicImage styleClass="link" value="img/delete.gif" />
</h:commandLink>
</h:column>
<f:facet name="footer">
<h:commandButton
actionListener="#{SessionBean.telefoneBean.setFormCreate}"
value="Inserir" />
</f:facet>
</h:dataTable>
<h:panelGrid columns="2"
rendered="#{!SessionBean.telefoneBean.formRead}"
styleClass="empty" footerClass="footer"
headerClass="header">
<f:facet name="header">
<h:outputText value="#{SessionBean.telefoneBean.titleForm}" />
</f:facet>
<h:column>
<h:outputText value="DDD" styleClass="form" />
</h:column>
<h:column>
<h:inputText
disabled="#{SessionBean.telefoneBean.formDestroy or SessionBean.telefoneBean.formView}"
value="#{SessionBean.telefoneBean.pojo.ddd}"
maxlength="2" />
</h:column>
<h:column>
<h:outputText value="Número" styleClass="form" />
</h:column>
<h:column>
<h:inputText
disabled="#{SessionBean.telefoneBean.formDestroy or SessionBean.telefoneBean.formView}"
value="#{SessionBean.telefoneBean.pojo.numero}"
maxlength="8" />
</h:column>
<h:column>
<h:outputText value="Nome" styleClass="form" />
</h:column>
<h:column>
<h:inputText
disabled="#{SessionBean.telefoneBean.formDestroy or SessionBean.telefoneBean.formView}"
value="#{SessionBean.telefoneBean.pojo.nome}"
maxlength="40" />
</h:column>
<f:facet name="footer">
<h:panelGrid styleClass="empty">
<h:column>
<h:commandButton
actionListener="#{SessionBean.telefoneBean.setFormRead}"
value="Voltar" />
<h:commandButton
actionListener="#{SessionBean.telefoneBean.create}"
value="Inserir"
rendered="#{SessionBean.telefoneBean.formCreate}" />
<h:commandButton
actionListener="#{SessionBean.telefoneBean.update}"
value="Alterar"
rendered="#{SessionBean.telefoneBean.formUpdate}" />
<h:commandButton
actionListener="#{SessionBean.telefoneBean.destroy}"
value="Excluir"
rendered="#{SessionBean.telefoneBean.formDestroy}" />
</h:column>
</h:panelGrid>
</f:facet>
</h:panelGrid>
<my:footer />
<h:messages showSummary="false" showDetail="true"
infoClass="severityInformation"
warnClass="severityWarning"
errorClass="severityError" />
</h:form>
</f:view>
</td>
</tr>
</table>
</body>
</html>
Para configurar nossa JSP como página inicial, execute a sequência:
- Após expandir o item Arquivos de configuração da aba Projetos, dê dois cliques sobre o arquivo web.xml
Entre na aba XML (parte superior do editor) e substitua o conteúdo pelo código abaixo:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<context-param>
<param-name>com.sun.faces.verifyObjects</param-name>
<param-value>false</param-value>
</context-param>
<context-param>
<param-name>com.sun.faces.validateXml</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>faces/crudTelefone.jsp</welcome-file>
</welcome-file-list>
</web-app>
O que transforma nosso JSP em página principal é a diretiva <welcome-file> localizada em <welcome-file-list>.
Vamos compreender o código da JSP. Acho que será mais didático, entender o que ocorre quando executamos este aplicativo. Neste ponto, o mesmo já está pronto para ser executado. Você poderá executar através do menu Executar, submenu Executar projeto principal. Quando fizer isso, o Netbeans levantará o container Tomcat, e abrirá um navegador. Algo similar ao mostrado abaixo aparecerá em sua tela:

Vejamos os detalhes:
- Quando o usuário acessa essa JSP, o container instanciaSessionBean (com escopo de sessão).
- Através das taglib no início do arquivo, definimos os namespace's – ou seja, quais tag poderemos utilizar no JSP (que na verdade, é um JSF – Java ServerFaces).
- Observe que através de <%@ taglib prefix="my" tagdir="/WEB-INF/tags" %>, podemos acessar nossas tags header e footer
- Na tag <my:header />, fazemos referência ao namespacemy (definido na taglib anterior), o conteúdo de header
- Em <f:view> definimos o escopo onde trabalharemos com os recursos JSF
- Em <h:form id="editForm"> é definido o formulário
- Na criação de <h:dataTable..., definimos os estilos CSS (styleClass, headerClass e footerClass), a listagemSessionBean.telefoneBean.list de componentes que será exibida (que obtém seu valor em getList() detelefoneBean), o nome do objeto (var="pojo") em cada linha da tabela, e se a mesma é renderizada. ComotelefoneBean foi criado agora, como você pode observar no código do constructor de ViewManager (“avô” deTelefoneMananger), definimos this.formRead = true;. Isso significa que, o código de SessionBean.telefoneBean.formRead (que acessa o métodoisFormRead() de ViewManager) utilizado em rendered, retorna true – e possibilita a exibição deste componente dataTable
- O campo <h:outputText value="#{SessionBean.telefoneBean.titleForm}" /> obtém o valor do método getTitleForm() que, por consequência de formRead ser verdadeiro, retorna a String Telefone – Consultar
- Os <h:outputText..., encontrados nas outras colunas, acessam diretamente o elemento pojo da iteração, ou seja, um objeto Telefone (lembrando que getList()de telefoneBean, definido no value de dataTable, retorna uma listagem de Telefone's. Em cada linha, um dos Telefone's será exibido. Como se trata do pojoTelefone, podemos acessar seus métodos getNome() e getNumeroFormatado(), respectivamente com pojo.nome e pojo.numeroFormatado
- Através dos <h:commandLink... (cada um associado com sua <h:graphicImage...), podemos disparar as ações (com actionListener) para setFormView(),setFormUpdate() e setFormDestroy(). Cada uma delas buscará da sessão, o objeto em questão (pelo código de getObjectBySession()), e tornará o respectivo booleano form ativo (desabilitando os demais com clearForm()). Isso nos levaria para as respectivas ações do CRUD.
- Através do <h:commandButton..., podemos executar a ação setFormCreate, que criará um novo pojo com createNewPojo() (que foi sobrescrito emTelefoneManager e, em runtime, será executado) e definirá formCreate como true
- Abaixo, na definição do <h:panelGrid..., o JSP não fará usa reenderização, pois o valor retornado em isFormRead() é true, e o código!SessionBean.telefoneBean.formRead, definido em rendered, pega o inverso (pelo símbolo !) de true
- Mais abaixo, em <my:footer />, nossa tag footer será exibida
- O código <h:messages..., mostrará as mensagens enviadas pelo código showMessage() de ViewManager (quando forem enviadas, o que neste momento, não ocorreu)
- O restante do código fecha as tags JSP e HTML
Como não existem objetos no banco, nada foi retornado. Se clicarmos em Inserir, a tela abaixo (similar, pois a tela mostrada já contém dados digitados e ações realizadas) será exibida:

Vejamos o que ocorreu:
- Quando o botão Inserir da primeira tela (a de consulta) foi clicado, o método setFormCreate() foi chamado, um novo Telefone foi criado e formCreate foi definido comotrue. Neste caso, a dataTable da consulta não será mais exibida (pois formRead foi definida como false) e opanelGrid passará a ser mostrado
- Os códigos dos <h:inputText..., serão associados aos valores do pojo Telefone
- Observe também que, através dos disabled's dos<h:inputText..., o JSP desabilitará os input's para os casos onde isFormView() ou isFormDestroy()retornarem true
- Com os <h:commandButton..., poderemos executar as ações do CRUD definidas em PojoManager
- Observe que cada um deles possui (com exceção doVoltar) um rendered que fará com que os mesmos só sejam exibidos em suas respectivas telas. Ou seja, como estamos na tela de cadastro, getFormCreate() retornará true e fará com que seu botão seja exibido. ComogetFormUpdate() e getFormDestroy() retornam false, os respectivos botões não serão mostrados para o usuário
- Simulando um cadastro, entre com os valores 2, 22334455 e João, para os campos DDD, Número e Nome, respectivamente
- Quando clicarmos no botão Inserir, o método create() de PojoManager será chamado
- PojoManager automaticamente já recebeu uma instância de Telefone através de setPojo(), e por isso, quando create() executa this.bo.create(this.pojo);,this.pojo retorna esta instância
- telefoneBean é uma instância de TelefoneManager, que por usa vez, define o Bo de PojoManager como sendo um TelefoneBo. Isso significa que, no runtime,this.bo de PojoManager é um TelefoneBo
- Ao verificarmos o método create() de Bo, vemos que o primeiro procedimento a ser executado é validate( pojo );
- Como Bo é um TelefoneBo, o validate() do runtime também é o seu, ou seja, vejamos o que ocorre no validate() de TelefoneBo
- Como já comentado, esse método faz a validação do pojo Telefone. Em primeiro lugar, ele valida o valor de telefone.getDdd() (através do objeto de utilidades util.Number), que por sua vez, retorna false em ddd.validate() e por consequência gera uma MyCrudException com a mensagem "Valor do DDD inválido!"
- Essa exceção é disparada pelo throws do método validate() de TelefoneBo, capturada pelo try do método create() de Bo, disparada pelo novoMyCrudException de create() de Bo, capturada pelo try de PojoManager, que por sua vez envia uma mensagem para a JSP
- Como nada foi alterado em formCreate (de ViewManager) a mesma tela será exibida, porém com a mensagem encaminha sendo mostrada na parte inferior (igual a imagem anterior)
- Se preenchermos os dados corretamente, após o validate() de TelefoneBo, o método create() de Dao é executado, que por sua vez, armazena o objeto no banco
- O runtime retorna para o método create() de PojoManager, executa setFormCreate() - para criar um novo Telefone e consequentemente limpar os campos do JSP – e posteriormente, joga uma mensagem para a JSP
Observe o que ocorre quando o cadastro é realizado com sucesso:

Esse fluxo é exatamente o mesmo que ocorre em Update(exceto pelo fato de que em vez de create(), é coordenado por update()). No procedimento de exclusão, algo similar acontece, porém, o métodovalidate() não é chamado. Outras telas representativas do processo são mostradas abaixo.

Ao voltarmos para a consulta, o registro será exibido com suas respectivas opções. Se clicarmos em excluir (ícone lixeira), a tela abaixo será exibida:

Se clicarmos na opção de alterar (ícone do disquete, na tela de consulta), e realizarmos algumas alterações, o método update() de PojoManager realiza o procedimento setFormRead(), que levará o usuário para a tela de consulta. Porém, como update() também exibe uma mensagem, a mesma será exibida na tela de consulta (conforme a tela abaixo). Se houvesse algum problema no processo, setFormRead() não seria executado e o usuário permaneceria na tela de atualização.

Nosso CRUD telefone está pronto. Conforme falado, vamos voltar ao código de TelefoneBo para aplicar algumas regras de negócio mais complexas.
Atenção: SEMPRE que você alterar o código (seja de JSP e, principalmente, Java) feche o navegador e execute (faça o deploy – publicação) a aplicação novamente.
Aplicando as Regras de Negócio
Como você pode observar, os procedimento de cadastro e atualização obrigam o usuário a passar pelo método validate() de TelefoneBo. É exatamente nesta classe que nós vamos aplicar as seguintes regras de negócio:
· O sistema não deve permitir cadastrar um DDD da área 22
· Luck é arquiinimigo de Dartvaider. O sistema não deve deixar que ambos existam no banco de dados para evitar uma pane no sistema
· O sistema deve ter no mínimo 1 (um) registro no banco de dados
Agora que já sabemos nossas regras de negócio (importantíssimas, aliás), vamos ao que interessa. Edite o código de TelefoneBo para que o mesmo fique igual ao contexto mostrado abaixo.
// TelefoneBo.java
package controller;
import java.util.List;
import model.InterfacePojo;
import model.Telefone;
import util.Number;
import util.MyCrudException;
public class TelefoneBo extends Bo {
public TelefoneBo() {
super( "selectTelefone" );
}
@Override
public void validate(InterfacePojo pojo) throws MyCrudException {
Telefone telefone = (Telefone) pojo;
// Validação do DDD
Number ddd = new Number( 2 , telefone.getDdd() );
if ( !ddd.validate() )
throw new MyCrudException( "Valor do DDD inválido!" );
// Validação do DDD
// Validação do Número
Number numero = new Number( 8 , telefone.getNumero() );
if ( !numero.validate() )
throw new MyCrudException( "Valor do Número inválido!" );
// Validação do Número
// Validação do Nome
if ( telefone.getNome() == null )
throw new MyCrudException( "Valor do Nome inválido!" );
if (
( telefone.getNome().length() < 1 ) ||
( telefone.getNome().length() > 40 )
) throw new MyCrudException( "Valor do Nome inválido!" );
// Validação do Nome
// Regras de Negócios
this.invalidDdd( telefone );
this.luckVersusDartvaider( telefone );
// Regras de Negócios
}
private void invalidDdd(Telefone telefone) throws MyCrudException {
if ( telefone.getDdd().equals( "22" ) )
throw new MyCrudException( "O DDD 22 não pode ser utilizado!" );
}
private void luckVersusDartvaider(Telefone telefone)
throws MyCrudException {
String ejbQL = "select obj from Telefone obj where ";
ejbQL += "UPPER( obj.nome ) like ?1";
String parameter[] = new String[ 1 ];
try {
if ( telefone.getNome().toUpperCase().equals( "LUCK" ) )
parameter[ 0 ] = "DARTVAIDER";
else
parameter[ 0 ] = "LUCK"
;
List<Telefone> collection = (List<Telefone>)
super.executeQuery( ejbQL , parameter );
if ( !collection.isEmpty() )
throw new MyCrudException( "May The Force Be With You!" );
} catch (Exception e) {
throw new MyCrudException( e.getMessage() );
}
}
@Override
public void destroy(InterfacePojo pojo) throws MyCrudException {
try {
String ejbQL = "select obj from Telefone obj";
List<Telefone> collection = (List<Telefone>)
super.executeQuery( ejbQL , null );
if ( collection.size() == 1 )
throw new MyCrudException( "Existe um único registro!" );
super.destroy( pojo );
} catch (Exception e) {
throw new MyCrudException( e.getMessage() );
}
}
}
Vamos ao seu entendimento. Para a primeira regra de negócio (DDD 22 inválido), uma simples verificação no pojo Telefone cadastrado (ou atualizado), onde comparamos o valor de getDdd() com 22, fará com que uma exception seja disparada. Neste caso, observe como ficaria a tela:

A próxima regra, que impede que estejam registrados os telefones de Luck e Dartvaider ao mesmo tempo, foi criada da seguinte maneira. Caso Luck já estivesse cadastrado (ou seja, cadastre Luck agora), ao tentarmos cadastrar Dartvaider, o fluxo abaixo ocorre:
- Na tela de cadastro, entre com o telefone de Dartvaider. Ao inserir, o método create(), em algum momento, encontra com validate(), que por sua vez chamaluckVersusDartvaider()
- Em luckVersusDartvaider(), criamos nossa EJB Query Language que vai verificar se existe algum registro no banco com obj.nome igual (ou que contém – like) o parâmetro, ainda não definido, ?1
- Se getNome() possui o valor LUCK, parameter[0] seráDARTVAIDER
- Caso contrário, paramter[0] será LUCK
- Em nosso exemplo, o valor de parameter[0] é igual áDARTVAIDER
- Através do método executeQuery() de Bo, passamosejbQL e o vetor parameter para executeQuery() de Dao, que por sua vez, monta uma Query que substitui a diretiva ?1 pelo valor contido em parameter (isso é realizado em um looping, pois podemos ter vários parâmetros dependendo da Query)
- Por fim, essa consulta retorna uma List<?> com os resultados
- O runtime volta para o método luckVersusDartvaider() e verifica a quantidade de registros contidos em collection. Como Luck já estava cadastrado (em nosso exemplo, já estava), collection.isEmpty() retornará false, que com a oposição do resultado (!), fará com que uma MyCrudException seja disparada com a mensagem "May The Force Be With You!"
- Observe que tanto o valor de getNome() (em Telefone) quanto o valor de obj.nome (na ejbQL) são transformados em maiúsculo para uma melhor comparação
Após executar este teste, a tela abaixo será exibida:

Como você pode observar, essa regra de negócio precisou realizar uma consulta mais específica no banco. Vamos, por fim, para a última regra. Como o sistema necessita de no mínimo um registro no banco, precisamos realizar essa regra no momento da exclusão de registros. Como faremos? Como você pode observar no código, o método destroy() de Bo foi sobrescrito em TelefoneBo e, através deste recurso acrescentamos a verificação da quantidade de registros através de consulta ao banco. A lógica da regra é simples e por isso não vou descrevê-la aqui. Observe o código que você compreenderá perfeitamente o que acontece. Como exemplo, tente excluir todos os registros e observe o vai acontecer quando você for tentar excluir o último registro:

Bom, é isso ai!
Para fazer um sisteminha podre desses eu preciso implementar isso tudo (Conclusão)?
A programação OO de Java, por si só requer uma quantidade maior de código para os sistemas simples como esse. Com o padrão MVC isso ocorre com mais impacto. Porém, neste exemplo, nós implementamos um Model com uma única classe. Imagine um sistema com 100 classes de modelo. Qual seria seu trabalho, a partir do que foi feito aqui, para implementar as outras 99 classes? Essa é a pergunta que você precisa responder! Por exemplo, para criar um CRUD Cliente, criaríamos a classe Cliente.java no pacote model, a classe CienteBo.java no pacote controller e a classe ClienteManager.java no pacote view. Como você pode observar, o código para implementar essas classes é mínimo. Por fim, você cria um gerenciamento para ClienteManager em SessionBean.java (criando um clienteBean, instanciando-o no constructor, e criando um método getClienteBean()) e colocaria uma referência para o mesmo no arquivo persistence.xml. Por último, você criaria a página crudCliente.jsp (que aliás, é muito similar à crudTelefone.jsp). Você é capaz de fazer isso em no máximo 30 minutos. Experimente! Você verá o ganho de produção conquistado ao desenvolver projetos Web com Java, EJB 3 no padrão MVC.
Bom pessoal! Espero ter contribuído com algo!
Caso vejam algum erro no sistema ou algum erro de português (ops!!!), peço que colaborem enviando-me um e-mail para que o mesmo possa ser corrigido!
Abraços, Guilherme Pontes.
Nenhum comentário:
Postar um comentário