DesenvolvimentoAplicacoesWebEmJava - 2ed - Final
DesenvolvimentoAplicacoesWebEmJava - 2ed - Final
W EB EM J AVA E O UTRAS
T ECNOLOGIAS
ATUALIZADO PARA J AKARTA EE 10
Segunda Edição
D AVID B UZATTO
I NSTITUTO F EDERAL DE E DUCAÇÃO, C IÊNCIA E T ECNOLOGIA DE S ÃO PAULO
C ÂMPUS S ÃO J OÃO DA B OA V ISTA
À Aurora,
luz da minha vida
S UMÁRIO
Prefácio v
I O Básico do Básico... 1
2 Processamento de Formulários 25
2.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.2 Formulários . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.3 Métodos HTTP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
2.3.1 Método GET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
2.3.2 Método POST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
2.3.3 Tratando Métodos HTTP . . . . . . . . . . . . . . . . . . . . . . 38
2.4 Resumo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
2.5 Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
2.6 Projetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
i
ii SUMÁRIO
Bibliografia 313
P REFÁCIO
Martin Fowler
P
REZADO leitor, seja bem-vindo! Os Capítulos deste livro foram organizados
de forma a guiá-lo no processo de fixação dos conteúdos aprendidos
durante a sua leitura por meio de exercícios, desafios e de projetos prá-
ticos aplicados no contexto de desenvolvimento de aplicações Web em
Java e outras tecnologias. A ordem dos Capítulos obedece a um caminho lógico que
será empregado para auxiliá-lo no processo de aprendizagem das tecnologias e técni-
cas discutidas, ou seja, a organização dos Capítulos segue a ordem cronológica dos
tópicos que serão apresentados, ensinados e treinados.
Antes de começar, eu gostaria de me apresentar. Meu
nome é David Buzatto e sou Bacharel em Sistemas de In-
formação pela Fundação de Ensino Octávio Bastos (2007),
Mestre em Ciência da Computação pela Universidade Fe-
deral de São Carlos (2010) e Doutor em Biotecnologia pela
Universidade de Ribeirão Preto (2017). Atualmente meus
principais interesses giram em torno de temas relaciona-
dos à construção de compiladores, teoria da computação,
análise e projeto de algoritmos, estruturas de dados, al-
goritmos em bioinformática e desenvolvimento de jogos
eletrônicos. Atualmente sou professor efetivo do Instituto Federal de Educação, Ciên-
cia e Tecnologia de São Paulo (IFSP), câmpus São João da Boa Vista. A melhor forma
de contatar é através do e-mail davidbuzatto@ifsp.edu.br.
Para que você possa aproveitar a leitura deste livro de forma plena, vale a pena enten-
der alguns padrões que serão utilizados neste texto. As quatro caixas apresentadas
v
vi PREFÁCIO
abaixo serão empregadas para mostrar, a você leitor, respectivamente, boas práticas
de programação, conteúdos complementares para melhorar e aprofundar seu apren-
dizado, observações pertinentes ao que está sendo apresentado e, por fim, itens que
precisam ser tratados com cuidado ou que podem acarretar em erros comuns de
programação.
| Boa Prática
Essa é uma caixa de “Boa Prática”.
| Saiba Mais
Essa é uma caixa de “Saiba Mais”.
| Observação
Essa é uma caixa de “Observação”.
| Atenção!
Essa é uma caixa de “Atenção!”.
Você notará que este este livro foi escrito de forma quase coloquial, com o objetivo
de conversar com você e não com o objetivo de ser um material de pesquisa. É de
suma importância que você procure implementar os exemplos apresentados e que
resolva cada um dos exercícios básicos de cada Capítulo, visto que a utilização de
uma linguagem de programação ou tecnologia, e mais importante ainda, a obtenção
de maturidade no desenvolvimento de software, é ferramenta primordial para o seu
sucesso profissional e intelectual.
Vale ressaltar que este livro e os projetos apresentados serão constantemente atualiza-
dos, sendo assim, sempre obtenha as últimas versões dos mesmos no endereço da
Web davidbuzatto.com.br. Espero que essa obra, de distribuição totalmente gratuita,
lhe seja útil!
Parte I
O Básico do Básico...
1
CAPÍTULO
1
J AVA PARA W EB
Lao-Tsé
N
ESTE Capítulo teremos como objetivos entender o funcionamento e a
arquitetura de aplicações Web desenvolvidas em Java, entender o fun-
cionamento dos Servlets e dos JSPs e aprender a configurar e a utilizar
a Integrated Development Environment (IDE) Apache NetBeans para o
apoio ao desenvolvimento de aplicações Web em Java.
1.1 Introdução
Para que você seja capaz de construir aplicações Web, primeiramente é preciso co-
nhecer como esse serviço é estruturado. A World Wide Web (WWW), ou simplesmente
Web, é um serviço executado em diversos computadores interligados em uma rede
mundial, sendo que em alguns desses computadores são executados programas cha-
mados de servidores, enquanto na maioria dos outros são executados programas
chamados de clientes, que se comunicam com os servidores que, por sua vez, servem
recursos para esses clientes. Na Figura 1.1 é ilustrado um recorte desta rede mundial.
3
4 CAPÍTULO 1. JAVA PARA WEB
Perceba que no recorte apresentado na Figura 1.1 são mostrados sete computadores,
sendo que quatro deles atuam como clientes e os outros três como servidores. É
importante entender que o que faz um computador ser cliente ou servidor é o tipo
de programa que está sendo usado/executado. No nosso exemplo, as máquinas que
atuam como servidores executam um programa denominado Servidor Web, que tem
a capacidade de servir (disponibilizar) aos outros computadores da rede, recursos que
fazem parte de uma aplicação Web, por exemplo, arquivos Hypertext Markup Lan-
guage (HTML), imagens em diversos formatos, arquivos de estilo, arquivos de script
etc. Os clientes, por sua vez, são, na maioria das vezes, os conhecidos navegadores
Web, ou browsers, que usamos no nosso dia a dia para acessar a Web e navegar em
diversos sites.
Da mesma forma que existem diversos navegadores, existem também alguns Ser-
vidores Web, sendo o Apache o mais famoso e o mais utilizado. Como já foi dito,
um Servidor Web tem a função de servir recursos requisitados pelos clientes. Vamos
aprender como isso funciona. Veja a Figura 1.2.
1.1. INTRODUÇÃO 5
protocolo://máquina/caminho_do_recurso
• Onde:
– protocolo: É a parte da URL que diz ao servidor qual o protocolo a ser utili-
zado. Quando acessamos páginas Web, por padrão, o protocolo utilizado é
o Hypertext Transfer Protocol (HTTP);
– máquina: É o nome ou o endereço codificado pelo Internet Protocol (IP)
da máquina que está executando o Servidor Web;
– caminho_do_recurso: É o caminho completo do recurso desejado que é
disponibilizado pelo servidor.
Confuso? Nem tanto. Vamos a um exemplo! Imagine a seguinte situação: Queremos
acessar o site do IFSP. Para isso, abra o seu navegador e preencha o campo endereço
com http://www.ifsp.edu.br/ e tecle <ENTER>. Fazendo isso, o navegador envia
uma requisição através de uma URL, usando o protocolo HTTP para a máquina
www.ifsp.edu.br, que por sua vez retorna ao navegador uma página HTML que
representa aquele endereço.
Perceba que não especificamos o caminho do recurso! Isso não foi necessário, pois os
Servidores Web são normalmente configurados para ter um comportamento padrão
para responder às requisições onde só seja especificado o nome da máquina e esse
comportamento padrão é direcionar para o recurso index.html, que é um arquivo
HTML. Portanto, usar o endereço http://www.ifsp.edu.br/ é o mesmo que usar
o endereço http://www.ifsp.edu.br/index.html. Faça um teste! Coloque o ende-
6 CAPÍTULO 1. JAVA PARA WEB
desencorajada hoje em dia. Como eu disse agora há pouco, tudo evolui. Para que os
desenvolvedores não precisassem mais gerar código HTML dentro do código Java de
um Servlet, foram inventadas as páginas JSP. Uma página JSP é um arquivo que pode
conter –e geralmente contém– código HTML e que pode interagir diretamente com
algumas funcionalidades de uma aplicação Web.
A rigor, um JSP é processado pelo Servidor de Aplicações e todo o seu conteúdo é
traduzido em um Servlet, que por sua vez é compilado e executado pelo Servidor. Todo
esse processo é realizado nos bastidores, então não precisamos nos preocupar com
esses detalhes, mas é sempre bom saber um pouquinho como as coisas funcionam
não é mesmo?
Por causa desse comportamento de tradução das JSPs para Servlets, nós podemos
inserir código Java dentro das JSPs, mas novamente não iremos usar essa abordagem,
muito menos irei ensinar como fazer, visto que da mesma forma que gerar HTML
dentro de um Servlet manualmente é desencorajado, essa abordagem de inserir código
Java dentro das JSPs também não deve ser utilizada. Uma JSP deve ser usada para exibir
dados, não para processá-los diretamente usando código Java. Note que não estou
dizendo que não iremos manipular dados dentro das JSPs, mas sim que existem formas
seguras e corretas para fazer isso. Aprenderemos esses detalhes só no Capítulo 3, pois
até lá, já teremos aprendido outros detalhes que ainda não foram apresentados.
Nesse passo, faremos a última configuração necessária, que consiste apenas em definir
o nome do usuário administrador do servidor. Na Figura 1.8 isso pode ser visto na
caixa de texto User Name: que está preenchida com “admin”. Ao preencher o nome
de usuário, clique em Finish .
1.4. PREPARAÇÃO DO AMBIENTE DE DESENVOLVIMENTO 13
Ao fazer isso, o GlassFish estará quase pronto para ser utilizado. A última coisa que
precisaremos fazer é copiar o Driver Java Database Conectivity (JDBC) do Sistema
Gerenciador de Banco de Dados (SGBD) MariaDB/MySQL que usaremos nos próximos
Capítulos deste livro.
Para isso, baixe o arquivo acessível através do link <https://repo1.maven.org/maven2
/org/mariadb/jdbc/mariadb-java-client/3.1.4/mariadb-java-client-3.1.4.jar> e, ao
terminar de baixar, copie-o para o diretório C:\Users\SeuUsuario\glassfish703
\glassfish\domains\domain1\lib\databases\. Agora sim, tudo pronto!
Caso haja alguma dúvida, visite o link mencionado que contém uma playlist de
tutoriais para a configuração de ambientes de desenvolvimento.
item Java with Ant e escolha Java Web . Na lista de tipos de projeto, escolha
Web Application e clique no botão Next > ;
• Passo 2: Preencha o campo Project Name: com “OlaMundoWeb” (sem acen-
tos, sem as aspas e tudo junto). Em Project Location: , defina o diretório onde
o projeto será salvo. Deixe a opção Use Dedicated Folder for Storing Libraries
marcada e clique no botão Next > ;
• Passo 3: Na opção Server: escolha o “GlassFish Server 7.0.3”, ou a opção com o
nome que você definiu ao registrar o GlassFish. Em Java EE Version: escolha
“Jakarta EE 10 Web”. Em Context Path: deixe o valor padrão (/OlaMundoWeb),
que é o mesmo nome que demos ao nosso projeto. Clique em Next > ;
• Passo 4: No último passo, o assistente perguntará quais frameworks nós quere-
mos inserir no nosso projeto. Nós não vamos usar nenhum, então basta clicar
em Finish . Fazendo isso, o novo projeto será criado e será aberto no NetBeans,
sendo que por padrão será criado um arquivo HTML (index.html) que será a
página inicial da nossa aplicação.
| Saiba Mais
Existem diversas definições para framework, sendo que, informalmente, pode-
mos definí-los como um conjunto de classes que incorporam uma abstração
que tem como objetivo de solucionar problemas de um tipo ou domínio especí-
fico.
Muito bem, criamos nosso primeiro projeto. Vamos executá-lo para ver o que acon-
tece? Na barra de ferramentas do NetBeans tem um botão com uma seta verde, igual a
um botão de “play” de um reprodutor de mídias. Quando você clicar nesse botão, você
vai ver que várias mensagens começarão a aparecer na janela de saída do NetBeans.
Essas mensagens irão mostrar para nós o que está acontecendo no momento, como a
inicialização do GlassFish (caso não esteja iniciado) etc. O que está esperando? Clique
lá no “play”. Assim que tudo estiver pronto, será aberta uma janela do seu navegador,
onde será mostrado o conteúdo do index.html, que no nosso caso será uma página
com “TODO write content” escrito.
Muito bem! Temos nossa primeira aplicação rodando no GlassFish! Fácil não é mesmo?
Por enquanto não vamos nos preocupar com a estrutura do projeto, iremos apren-
der os detalhes aos poucos. Vamos colocar um pouco de código HTML no nosso
index.html? Ele deve estar aberto no NetBeans. Se não estiver, procure o arquivo
index.html na pasta Web Pages do seu projeto e clique duas vezes no arquivo para
abri-lo no editor. Vamos mudar o título, escrevendo “Meu Primeiro Projeto Java para
Web” no lugar de “TODO supply a title” e dentro da tag <body> do arquivo, inserir um
1.4. PREPARAÇÃO DO AMBIENTE DE DESENVOLVIMENTO 15
heading <h1> e um parágrafo com um link para o site do IFSP. Veja na Listagem 1.1
como deve ficar seu código.
1 <!DOCTYPE html>
2
3 <html>
4 <head>
5 <title>Meu Primeiro Projeto Java para Web</title>
6 <meta charset="UTF-8">
7 <meta name="viewport" content="width=device-width, initial-scale=1.0">
8 </head>
9 <body>
10 <h1>Olá Mundo!</h1>
11 <p>
12 <a href="http://www.ifsp.edu.br/">Site do IFSP</a>
13 </p>
14 </body>
15 </html>
Poderíamos ter usado um arquivo JavaServer Pages (JSP) ao invés de usar um arquivo
HTML, permitindo a existência de outros tipos de estruturas que vamos aprender
no decorrer do livro, mas por enquanto vamos manter o HTML. Vamos testar os
Servlets agora? Como primeiro exemplo, nós vamos criar um Servlet manualmente,
enquanto os outros que vamos desenvolver durante o nosso curso serão feitos usando
um assistente do NetBeans, mas essa forma fácil nós só vamos aprender a partir do
Capítulo 2.
Aprenderemos como criar manualmente um Servlet, para que possamos aprender
alguns detalhes importantes sobre o funcionamento de aplicações Web feitas em Java.
Siga os passos abaixo:
• Passo 1: Na árvore que representa a estrutura do projeto, procure pela pasta
Source Packages e expanda-a (clique no sinal de “+” à esquerda). Dentro dela
1.4. PREPARAÇÃO DO AMBIENTE DE DESENVOLVIMENTO 17
haverá um pacote com o ícone cinza chamado <default package> . Como vocês
devem saber, é desencorajado que se trabalhe com pacotes padrão em Java, en-
tão vamos criar um pacote. Clique com o botão direito na pasta Source Packages
e escolha New , procure pela opção Java Package... e clique nela. Se esta op-
ção não estiver sendo exibida, clique na opção Other... (no final da lista), es-
colha Source Packages nas categorias, e Java ‘Package nos tipos de arquivos
e clique em Next > . Preencha o campo Package Name: com “olamundoweb”
(sem as aspas) e clique em Finish . O pacote será criado;
• Passo 2: Repita o Passo 1, só que agora clicando com o botão direito no pacote
que você criou e crie um pacote chamado “servlets” (sem as aspas). O nome
do pacote deverá ser preenchido com “olamundoweb.servlets”. Seu projeto
agora terá um pacote chamado “olamundoweb.servlets”. O resultado desses dois
primeiros passos podem ser vistos na Figura 1.10;
1 package olamundoweb.servlets;
2
3 public class OlaServlet {
4
5 }
18 CAPÍTULO 1. JAVA PARA WEB
• Passo 4: Para que uma classe seja um Servlet, precisamos estender a classe
HttpServlet, que está contida no pacote jakarta.servlet.http4 e então
implementar os métodos HTTP que queremos que nosso Servlet trate. Não se
preocupe, ainda vamos aprender sobre os métodos HTTP, então o mais impor-
tante a saber, por enquanto, é que os métodos HTTP mais usados são o GET
( doGet(...) de HttpServlet) e o POST ( doPost(...) de HttpServlet).
Então teremos que sobrescrever cada um desses métodos e ainda criaremos um
terceiro que será invocado a partir dos outros dois. Confuso? Vamos ver como o
código ficaria. Leia os comentários e copie o código da Listagem 1.3 para o seu
editor.
1 package olamundoweb.servlets;
2
3 import java.io.IOException;
4 import jakarta.servlet.ServletException;
5 import jakarta.servlet.annotation.WebServlet;
6 import jakarta.servlet.http.HttpServlet;
7 import jakarta.servlet.http.HttpServletRequest;
8 import jakarta.servlet.http.HttpServletResponse;
9
10
11 /**
12 * Nosso primeiro Servlet!
13 *
14 * A anotação @WebServlet é usada para indicar que esta classe
15 * é um Servlet, configurando seu nome o padrão de URL que
16 * será associado à esse componente.
17 *
18 * @author Prof. Dr. David Buzatto
19 */
20 @WebServlet( name = "OlaServlet", urlPatterns = { "/ola" } )
21 public class OlaServlet extends HttpServlet {
22
23 /**
24 * Método para tratar requisições que usam o método
25 * GET do protocolo HTTP. É um método herdado da
26 * classe HttpServlet que precisa ser sobrescrito.
O Jakarta EE 10 usa o novo namespace do Java EE, onde o pacote base é o jakarta. Antes da
4
27 *
28 * A anotação @Override indica ao compilador que
29 * estamos sobrescrevendo um método herdado da
30 * classe que está sendo estendida, no caso
31 * HttpServlet.
32 *
33 * @param request Referência ao objeto que contém
34 * os dados da requisição.
35 * @param response Referência ao objeto que conterá
36 * os dados da resposta.
37 *
38 * @throws ServletException
39 * @throws IOException
40 */
41 @Override
42 protected void doGet(
43 HttpServletRequest request,
44 HttpServletResponse response )
45 throws ServletException, IOException {
46
47 // chama o método processRequest
48 processRequest( request, response );
49
50 }
51
52 /**
53 * Método para tratar requisições que usam o método
54 * POST do protocolo HTTP.
55 *
56 * @param request Referência ao objeto que contém
57 * os dados da requisição.
58 * @param response Referência ao objeto que conterá
59 * os dados da resposta.
60 *
61 * @throws ServletException
62 * @throws IOException
63 */
64 @Override
65 protected void doPost(
66 HttpServletRequest request,
67 HttpServletResponse response )
68 throws ServletException, IOException {
20 CAPÍTULO 1. JAVA PARA WEB
69
70 // chama o método processRequest
71 processRequest( request, response );
72
73 }
74
75 protected void processRequest(
76 HttpServletRequest request,
77 HttpServletResponse response )
78 throws ServletException, IOException {
79
80 /*
81 * Aqui vem o código que queremos que o
82 * nosso Servlet execute.
83 */
84 System.out.println( "Ola Mundo!" );
85 System.out.println( "Meu Primeiro Servlet!" );
86
87 }
88
89 }
Até agora criamos uma classe chamada OlaServlet, que estende a classe HttpServlet.
Sobrescrevemos os métodos doGet(...) e doPost(...) herdados de HttpServlet
que tratam respectivamente os métodos GET e POST do protocolo HTTP e criamos
um terceiro método, chamado processRequest(...) , que tem a mesma assina-
tura dos métodos doGet(...) e doPost(...) e que é invocado dentro deles. É
no processRequest que iremos colocar o código que queremos executar, sendo
que no nosso exemplo, estamos mandando imprimir na saída duas Strings: “Olá
Mundo!” e “Meu Primeiro Servlet!”. Ou seja, se chamarmos o Servlet usando o mé-
todo GET, o método doGet(...) será invocado e passará o controle para o método
processRequest(...) que irá imprimir as mensagens na saída. O mesmo acontece
para o método POST.
Muito bem, você tem um Servlet totalmente funcional, mas ai você se pergunta:
“Como vou chamar esse Servlet através do navegador?”. Então eu respondo: no có-
digo completo, você percebeu que há uma anotação chamada @WebServlet ? É
1.4. PREPARAÇÃO DO AMBIENTE DE DESENVOLVIMENTO 21
5
Antigamente precisávamos fazer o mapemanto em um arquivo Extensible Markup Language
(XML) (<http://pt.wikipedia.org/wiki/XML>) chamado de Descritor de Implantação (DI, em inglês
Deployment Descriptor), representado pelo arquivo web.xml, o que atualmente, com as versõs mais
novas do Jakarta/Java EE, não é mais necessário para algumas situações.
6
Esse sufixo é inserido automaticamente pelo servidor.
22 CAPÍTULO 1. JAVA PARA WEB
Por mais que nosso exemplo não tenha nenhuma utilidade aparente, ele foi impor-
tante para nós entendermos o funcionamento básico de uma aplicação Web feita em
Java. Nos próximos Capítulos vamos colocar o que aprendemos em prática, além de
aprender várias outras coisas com o objetivo de criarmos um sistema de cadastro na
forma de uma aplicação Web. Não se esqueça de praticar o que aprendemos até agora.
1.5 Resumo
Neste Capítulo aprendemos o que é e como funciona uma aplicação Web em Java.
Aprendemos a criar nosso primeiro projeto e alguns detalhes sobre a tecnologia que
estamos utilizando. Executamos nossa aplicação e fizemos algumas modificações
nela para vermos o que estava sendo feito. Criamos também –de forma manual– um
Servlet, que como aprendemos é um dos componentes principais de uma aplicação
Web em Java.
1.6. EXERCÍCIOS 23
1.6 Exercícios
Exercício 1.2: Como são chamados os clientes que utilizamos para acessar aplicações
servidas por um Servidor Web? Cite alguns exemplos.
1.7 Projetos
Projeto 1.1: Crie um novo projeto Java Web no NetBeans, com o nome de “MinhaPa-
gina”, edite o index.html de modo a exibir seus dados pessoais, seus interesses etc.
Tente inserir uma imagem também. Dica: a imagem deve estar dentro do projeto do
NetBeans. Pense se você entende o motivo pelo qual o arquivo index.html é mos-
trado por padrão quando você acessa sua página através da URL <HTTP://localhost:
8080/MinhaPagina>.
Projeto 1.2: Crie um novo projeto Java Web no NetBeans, com o nome de “Conta-
dor”. Nesse projeto você deve criar um Servlet manualmente e dentro do método
processRequest(...) use uma estrutura de repetição para direcionar para a saída
padrão os números de 1 a 30.
Projeto 1.3: Crie um novo projeto Java Web no NetBeans, com o nome de “Fibo-
nacci”. Nesse projeto, você deve criar um Servlet manualmente e dentro do método
processRequest(...) use uma estrutura de repetição para exibir os 30 primeiros
termos da série de Fibonacci. Crie um método chamado fibonacci dentro do seu
Servlet, sendo que este método deve receber como parâmetro um inteiro e retornar
um inteiro. O inteiro que é recebido como parâmetro é o número do termo desejado,
enquanto o inteiro que é retornado é o termo correspondente ao parâmetro que foi
recebido. A série de Fibonacci é formada inicialmente pelos números 1 e 1, sendo
que os próximos números da série são gerados a partir da soma dos dois números
anteriores. Os sete primeiros termos da série de Fibonacci são 1, 1, 2, 3, 5, 8, 13, onde:
2 = 1 + 1, 3 = 1 + 2, 5 = 2 + 3, 8 = 3 + 5, 13 = 5 + 8.
Exemplos de chamadas da função fibonacci:
• fibonacci(2): retorna 1
• fibonacci(5): retorna 5
• fibonacci(7): retorna 13
CAPÍTULO
2
P ROCESSAMENTO DE F ORMULÁRIOS
Bill Gates
N
ESTE Capítulo teremos como objetivos entender o funcionamento de
formulários HTML e conseguirmos diferenciar os métodos do protocolo
HTTP e como tratá-los.
2.1 Introdução
Neste Capítulo vamos começar a aprender a criar algo útil! No Capítulo 1, aprendemos
as bases do desenvolvimento Web em Java, criamos alguns programas de brinquedo1
para aplicar as técnicas que aprendemos e agora vamos aprender mais alguns detalhes,
mas dessa vez nossos programas serão mais elaborados. Vamos começar?
1
Programa de brinquedo é todo programa criado para apresentar algum conceito e que, normal-
mente, não tem uma utilidade prática além da pedagógica.
25
26 CAPÍTULO 2. PROCESSAMENTO DE FORMULÁRIOS
2.2 Formulários
A forma tradicional de se desenvolver aplicações para Web que interajam com o servi-
dor é utilizando os chamados formulários. Um formulário é composto normalmente
por um conjunto de componentes que permitem que o usuário forneça dados para
serem submetidos ao servidor. Quando esses dados são recebidos pelo servidor, algum
componente da aplicação vai tratá-los, sendo que no nosso caso, esse componente
vai ser implementado na forma de um Servlet.
Atualmente existem diversas técnicas para a criação de aplicações Web, sendo que,
dependendo da técnica/tecnologia, a forma de submeter dados ao servidor é diferente.
Uma dessas técnicas é o chamado Asynchronous JavaScript and XML (AJAX) que hoje
em dia é implementado de inúmeras formas. Neste livro aprenderemos sobre AJAX no
Capítulo 7.
Abra o NetBeans e crie um novo projeto Java Web. Dê o nome de “PrimeiroFormulario”
(sem as aspas). Sempre que for criar um novo projeto, siga os passos descritos na
Subseção 1.4.3 do Capítulo 1. Com o projeto criado, vamos editar o index.html. Nele
vamos alterar o título da página e criar nosso primeiro formulário, que será usado
para preenchermos dados pessoais de um cliente. Veja na Listagem 2.1 como ficou o
código. Não se esqueça de copiá-lo no seu index.html.
1 <!DOCTYPE html>
2
3 <html>
4 <head>
5 <title>Meu Primeiro Formulário</title>
6 <meta charset="UTF-8">
7 <meta name="viewport" content="width=device-width, initial-scale=1.0">
8 </head>
9 <body>
10
11 <h1>Dados Pessoais do Cliente</h1>
12
13 <form>
14
15 <label>Nome: </label>
16 <input type="text" size="20" name="nome"/>
17 <br/>
18
2.2. FORMULÁRIOS 27
19 <label>Sobrenome: </label>
20 <input type="text" size="20" name="sobrenome"/>
21 <br/>
22
23 <label>CPF: </label>
24 <input type="text" size="15" name="cpf"/>
25 <br/>
26
27 <label>Data de Nascimento: </label>
28 <input type="text" size="10" name="dataNasc"/>
29 <br/>
30
31 <label>Sexo: </label>
32 <input type="radio" name="sexo" value="M"/> Masculino
33 <input type="radio" name="sexo" value="F"/> Feminino
34 <br/>
35
36 <label>Observações: </label>
37 <br/>
38 <textarea cols="40" rows="10" name="observacoes"></textarea>
39 <br/>
40
41 <input type="submit" value="Enviar Dados"/>
42
43 </form>
44
45 </body>
46 </html>
Copiou? Salve o arquivo e execute a aplicação. Você vai ter algo como o mostrado na
Figura 2.1.
28 CAPÍTULO 2. PROCESSAMENTO DE FORMULÁRIOS
Quanta coisa! O formulário não ficou uma obra prima, mas esse não é nosso objetivo
agora. Precisamos entender o que cada tag faz. Vamos agora analisar o código da
Listagem 2.1 e entender o protótipo que fizemos. Irei detalhar apenas as tags <form>
e seus componentes, pois acredito que você já conheça as outras que foram utilizadas.
Vamos lá então:
• Linha 13: Nesta linha abrimos a tag <form> que delimita um formulário
HTML. Note que fechamos a tag <form> na linha 43. Todas as tags que forem
inseridas entre <form> e </form> farão parte do formulário;
• Linha 15: Criamos um label (tag <label> ) com o conteúdo “Nome: ”. A tag
<label> é usada para criar um rótulo. Ao invés de usar a tag <label> , pode-
ríamos simplesmente ter inserido o texto que queremos mostrar no formulário,
mas como o texto que estamos utilizando tem o propósito de ser um rótulo para
2.2. FORMULÁRIOS 29
um campo do formulário, iremos utilizar essa tag para deixar nosso código mais
organizado e inserir uma certa carga semântica no nosso código;
• Linha 16: Criamos um input (campo de entrada) do tipo text (texto) com tama-
nho de 20 colunas e com o nome de “nome”. Dentre as tags que representam
componentes nos formulários, a <input> é uma delas. Existem vários tipos de
inputs, diferenciados pela propriedade type, e que vamos aprender aos poucos.
A propriedade size, como você deve ter percebido, é utilizada para configurar
a largura do campo de texto. A propriedade name é utilizada pelo navegador
para identificar os dados do componente em questão no momento de enviar os
dados para o servidor. Não entendeu a utilidade da propriedade name? Não se
preocupe, logo vai fazer sentido;
• Linha 17: Usamos a tag <br/> para pular uma linha;
• Linhas 19 a 29: Os próximos três campos (sobrenome, CPF e data de nasci-
mento) são bem parecidos com o primeiro;
• Linha 32: Criamos um input do tipo radio (botão de rádio), com nome configu-
rado como “sexo” e com o valor (value) configurado com “M”;
• Linha 33: Idem à linha anterior, com a diferença que o valor é “F”. Note que
a propriedade name de ambos os radios é a mesma, pois eles representam
o mesmo campo (sexo). Perceba que no navegador, se você selecionar um
deles e depois clicar no outro, o que estava selecionado previamente deixa de
ser selecionado. Se a propriedade name for diferente, eles serão considerados
campos diferentes e então esse comportamento da seleção não existirá. Note
ainda que você pode “amarrar” quantos radios você precisar;
• Linha 38: Nessa linha definimos uma área de texto. Esse componente, repre-
sentado pela tag <textarea> é utilizado, como o próprio nome já diz, para
criar uma área de texto livre, onde o usuário poderá digitar uma quantidade
arbitrária de texto. Note que para utilizar um <textarea> nós precisamos usar
a tag de fechamento ( </textarea> ), ao invés de fazer da forma que estamos
fazendo com os inputs. A novidade nesse componente são as propriedades cols
e rows, que são usadas respectivamente para definir a quantidade de colunas e
de linhas do componente;
• Linha 41: Por fim, nessa linha definimos um input do tipo submit, que é um
botão que tem o comportamento padrão de, ao ser clicado, submeter (enviar)
os dados do formulário para o servidor. Note que usamos a propriedade value
para definir o texto do botão.
Agora que já conhecemos alguns dos componentes que podemos utilizar nos nossos
formulários, mas você deve estar se perguntando: “Tudo bem, o input do tipo submit
é usado para enviar os dados do formulário para o servidor, mas onde eu digo ao
submit para onde os dados do formulário devem ser enviados?”. Vamos à resposta!
30 CAPÍTULO 2. PROCESSAMENTO DE FORMULÁRIOS
Eu tenho dito várias vezes que o componente que vai tratar os dados de um formulário
na nossa aplicação é o Servlet não é mesmo? Então precisamos criar um Servlet que
vai receber esses dados e então configurar o formulário para direcionar os dados
inseridos nele para o Servlet apropriado.
Vamos criar o Servlet, mas agora iremos fazer de uma forma mais automática do que
a que estamos fazendo desde que aprendemos a trabalhar com os Servlets. Na pasta
de pacotes de código-fonte do projeto, crie um pacote chamado “primeiroformula-
rio.servlets” (sem as aspas). Clique com o botão direito no pacote criado, escolha
New e procure pela opção Servlet... . Se não encontrou, escolha a opção Other... ,
selecione Web na categoria, Servlet no tipo de arquivo e clique em Next > .
| Atenção!
Infelizmente o NetBeans ainda não lida apropriadamente com a mudança
do namespace do Jakarta EE 9 em diante, então as importações dos Servlets
e de outros componentes Web provavelmente serão criadas erroneamente,
ou seja, usando o pacote base javax ao invés do jakarta. Faça essa alteração
manualmente na lista de imports das classes, ou seja, troque javax por jakarta
onde for necessário.
Click on the + sign on the left to Edit the code.”. Siga a sugestão da frase e clique no
“+”. O que apareceu? A implementação dos métodos GET e POST, sendo que dentro
delas é chamado o processRequest(...) ! Viu só? Da mesma forma que fazíamos
manualmente! Não iremos mexer ali, então você pode contrair novamente esta seção
do código clicando no sinal de “–”.
Note que além de implementar o esqueleto do nosso Servlet, o NetBeans também in-
seriu um trecho de código dentro do processRequest(...) . O código que está inse-
rido configura o que o Servlet gerará de resposta a quem o requisitou
( response.setContentType(...) ), obtém o fluxo de escrita do Servlet, escreve
uma série de Strings que representam uma página HTML nesse fluxo e o fecha auto-
maticamente, dado o uso do try with resources. Você se lembra que já falei algumas
vezes que não iremos implementar Servlets que geram código HTML? Então, vamos
limpar esse método, tirando todo o código que foi inserido dentro dele. Vá no editor e
apague o conteúdo entre as linhas 34 ( response.setContentType(...) ) e 46 (}),
incluindo elas. Seu processRequest(...) deve estar vazio agora.
Antes de escrevermos nosso código, vamos a mais um pouquinho de teoria. Você se
lembra, lá no comecinho do Capítulo 1, que eu falei que o cliente manda uma requisi-
ção para o servidor e ele manda uma resposta? Nos Servlets, essa requisição e essa res-
posta são representadas respectivamente por objetos do tipo HttpServletRequest
e HttpServletResponse. Note que os três métodos implementados nos nossos Ser-
vlets ( processRequest(...) , doGet(...) e doPost(...) ) recebem dois pa-
râmetros, sendo eles dos tipos que mencionei. Qual a conclusão que você chega
então? Os dados enviados pelo cliente ao servidor estão dentro do objeto do tipo
HttpServletRequest, que chamamos de request no nosso código e os dados que
enviamos de volta ao cliente, ou a quem invocou o Servlet, devem ser inseridos no
objeto do tipo HttpServletResponse, que demos o nome de response.
Sabendo disso, agora ficou fácil! Os dados do formulário de clientes que estamos cons-
truindo serão recebidos pelo nosso Servlet através do objeto apontado por request!
Legal, mas ainda falta um detalhe... Não informamos ao navegador qual o destino dos
dados do formulário! Vamos fazer isso agora. Volte no index.html e procure pela tag
<form> (a mesma que está na linha 13 da Listagem 2.1). Para informarmos ao nave-
gador qual o destino do dados do formulário, utilizamos a propriedade action, sendo
que nesta propriedade, colocamos a URL do componente que deve tratar os dados
do formulário. Mapeamos nosso Servlet usando o padrão /processaDadosCliente
não foi? Então, qual será a URL do nosso Servlet? Resposta: processaDadosCliente.
Vamos editar nossa tag <form> então, configurando a propriedade action para a
URL citada. Na Listagem 2.2 você pode ver como ficou o código da tag <form> . Note
que não estou listando todo o arquivo.
32 CAPÍTULO 2. PROCESSAMENTO DE FORMULÁRIOS
Isso quer dizer que, quando clicarmos no botão “Enviar Dados” (que é um input
do tipo submit), os dados que estiverem nos componentes que estão dentro desse
formulário serão enviados para o recurso processaDadosCliente, que é o endereço
do nosso Servlet! Já alterou o arquivo? Salvou? Legal! Atualize a página, preencha os
campos e clique no botão “Enviar Dados”. O que aconteceu? Uma página em branco
foi exibida não é? E na barra de endereços, o que apareceu? O endereço do Servlet
mais um monte de “coisas”! Os detalhes sobre isso nós iremos aprender na próxima
seção, então não se preocupe por enquanto.
Você se lembra dos nossos primeiros exemplos? Acessávamos o endereço do Servlet,
uma página em branco era exibida e duas Strings eram direcionadas para a saída do
servidor, lembra? Quem fazia esse direcionamento era o Servlet, não era? Vamos fazer
a mesma coisa com o nosso Servlet de dados dos clientes, mas mostraremos os dados
que foram preenchidos nos formulários. Vamos lá então?
Volte ao Servlet, vamos implementar o método processRequest(...) . Qual é
mesmo o nome do parâmetro do método processRequest(...) que armazena os
dados enviados pelo cliente? É o request certo? Veja o código da Listagem 2.3. Leia
todos os comentário que fiz.
1 package primeiroformulario.servlets;
2
3 import java.io.IOException;
4 import jakarta.servlet.ServletException;
5 import jakarta.servlet.annotation.WebServlet;
6 import jakarta.servlet.http.HttpServlet;
7 import jakarta.servlet.http.HttpServletRequest;
8 import jakarta.servlet.http.HttpServletResponse;
9
10 /**
11 * Servlet para processamento dos dados do formulário.
12 *
2.2. FORMULÁRIOS 33
getParameter( String param ) , que é uma String, é o nome que foi dado ao
componente do formulário, que no caso foi “nome”. Estude as linhas 42, 43, 44, 45
e 46 e tente fazer um paralelo com o formulário contido no index.html. Note que
a String que é passada como parâmetro no método getParameter(...) sempre
reflete o nome dado ao componente do formulário através da propriedade name. Veja
a linha 44. Declaramos uma variável chamada dataNascimento que é inicializada
com o valor do parâmetro “dataNasc” (configurado no formulário).
O que fizemos até a linha 46 foi criar uma variável que vai receber o valor de cada
componente do formulário. A partir da linha 48, até o final do método, direcionamos
para a saída os dados que foram obtidos. Vamos testar? Salve o Servlet e execute
o projeto (botão de “play”, lembra?). Na página, preencha o formulário, clique em
“Enviar Dados” e volte no NetBeans para ver o que aconteceu. Olhe na janela de saída!
Lá estão os dados que você preencheu no formulário! Muito bem! Imagine agora
se esse fosse um sistema real. Esses dados recebidos dentro do Servlet poderiam
alimentar uma query SQL para inserir esse cliente em um banco de dados! Legal não
é?! A partir desse ponto, você já deve estar entendendo melhor o que está acontecendo,
mas e aquele monte de coisas escritas no endereço do navegador depois de clicar em
“Enviar Dados”? Esse comportamento está intrinsecamente ligado ao tipo de método
HTTP que estamos usando. Vamos para a próxima Seção, vou explicar isso lá.
| Saiba Mais
Quer conhecer os outros métodos HTTP como HEAD, PUT, DELETE etc.? Acesse
este link: <https://developer.mozilla.org/pt-BR/docs/Web/HTTP/Methods>
por “&”. Então temos: nome igual a “Juca”, sobrenome igual a vazio, cpf igual a vazio,
dataNasc igual a vazio, sexo igual a “M” e observacoes igual a vazio. A saída no
NetBeans deve ter ficado assim:
Dados do Cliente:|#]
Nome: Juca|#]
Sobrenome: |#]
CPF: |#]
Data de Nascimento: |#]
Sexo: Masculino|#]
Observações: |#]
Vamos mandar a requisição novamente para o NetBeans, só que agora modificando
a URL ao invés de usar o formulário. Dê o sobrenome de “Santos” ao Juca e defina o
CPF como 123456789. Preencheu a URL na barra de endereços? Tecle <ENTER> e veja
o que aconteceu no NetBeans. A saída deve ter sido essa aqui:
Dados do Cliente:|#]
Nome: Juca|#]
Sobrenome: Santos|#]
CPF: 123456789|#]
Data de Nascimento: |#]
Sexo: Masculino|#]
Observações: |#]
Então, basicamente, ao usarmos o método GET, indicamos que queremos algum
recurso do servidor. Quando enviamos dados através do método GET, esses dados,
na forma de parâmetros, são codificados na própria URL. O nosso formulário do
index.html utiliza por padrão o método GET. Qualquer formulário usa por padrão o
método GET, mas se quisermos mudar o método de envio do formulário, precisamos
usar a propriedade method da tag <form> . Ai você me pergunta: Porque usaríamos
outro método? O GET já não funciona? E eu respondo: Sim, o GET funciona, mas
imagine a seguinte situação: você vai armazenar os dados de um usuário de um
sistema. Você vai mandar vários dados para o servidor, inclusive uma senha. O que
acontece? A senha enviada vai aparecer na URL! Afinal, a senha é um campo do
formulário! O ideal seria ninguém a ver correto? Outro problema. O tamanho de uma
URL é fixo! Então não podemos mandar conteúdos de tamanho arbitrário, visto que
iremos perder dados caso usemos o método GET! Imagine mandar um vídeo para
publicação no YouTube! Então como fazemos? Método POST, ao resgate!
38 CAPÍTULO 2. PROCESSAMENTO DE FORMULÁRIOS
2.4 Resumo
Neste Capítulo demos um passo muito importante para a nossa vida como desenvol-
vedores Web, pois aprendemos a trabalhar com formulários e entendemos o funcio-
namento dos métodos GET e POST que fazem parte do protocolo HTTP. Como você
já deve ter percebido, os formulários desempenham um papel importantíssimo nas
aplicações Web. Tenho certeza que de agora em diante, sempre que você usar uma
aplicação Web, você saberá como aquele formulário funciona. Para colocar em prática
o que aprendemos, criamos um projeto Java Web no NetBeans e fizemos diversos
testes.
2.5 Exercícios
Exercício 2.1: Qual a diferença entre os métodos GET e POST? Quando devemos utilizar
um ou o outro?
40 CAPÍTULO 2. PROCESSAMENTO DE FORMULÁRIOS
2.6 Projetos
Projeto 2.1: Incremente o projeto que criamos durante o Capítulo inserindo mais
alguns campos no formulário: email, logradouro, número, complemento, cidade,
estado, CEP, se o cliente tem ou não filhos. Utilize apropriadamente os tipos de input
que aprendemos até agora.
Projeto 2.2: Crie um novo projeto Java Web, com o nome de “FormularioDVD” (sem
as aspas), que deve ter um formulário usado para enviar dados de um DVD. Um DVD,
no nosso caso, deve ter: número, título, ator/atriz principal, ator/atriz coadjuvante,
diretor/diretora e ano de lançamento. Para tratar o formulário, crie um Servlet usando
o assistente do NetBeans. Esse Servlet deve obter os dados enviados através do for-
mulário e imprimi-los na saída padrão usando System.out.println(...) , como
foi feito no exemplo construído durante este Capítulo. Esse formulário deve usar o
método POST.
Projeto 2.3: Crie um novo projeto Java Web, com o nome de “FormularioProduto”
(sem as aspas), que deve ter um formulário usado para enviar dados de um Produto.
Um Produto, no nosso caso, deve ter: código de barras, descrição, unidade de me-
dida (unidade ou kg), quantidade por embalagem, fabricante (nome). Para tratar o
formulário, crie um Servlet usando o assistente do NetBeans. Esse Servlet deve ob-
ter os dados enviados através do formulário e imprimi-los na saída padrão usando
System.out.println(...) , como foi feito no exemplo construído durante este
Capítulo. Esse formulário deve usar o método POST.
Projeto 2.4: Crie um novo projeto Java Web, com o nome de “CalculadoraWeb” (sem
as aspas), que deve ter um formulário usado para atuar como uma calculadora. Nesse
formulário, deve haver dois campos (“número 1” e “número 2”) e um conjunto de
radios para representar a operação a ser realizada (adição, subtração, multiplicação
e divisão). Para tratar o formulário, crie um Servlet usando o assistente do NetBe-
ans. Esse Servlet deve obter os dados enviados através do formulário, executar a
operação escolhida pelo usuário e imprimir o resultado na saída padrão usando
System.out.println(...) , como foi feito no exemplo construído durante este
Capítulo. Esse formulário deve usar o método GET. Faça testes de envio dos dados
usando apenas a URL gerada depois da primeira submissão.
Projeto 2.5: Crie um novo projeto Java Web, com o nome de “TamanhoString” (sem
as aspas), que deve ter um formulário com apenas um campo usado para enviar
uma String de qualquer tamanho para um Servlet. Utilize uma <textarea> para
o usuário poder inserir essa String no formulário. O Servlet deve obter a String
enviada e imprimir a quantidade de caracteres da String na saída padrão usando
2.6. PROJETOS 41
Projeto 2.6: Crie um novo projeto Java Web, com o nome de “EhPrimo” (sem as aspas),
que deve ter um formulário usado para enviar um número inteiro para um Servlet,
que por sua vez deve verificar se este número é primo. O resultado do teste deve ser
impresso na saída padrão. Esse formulário deve usar o método GET.
Projeto 2.7: Crie um novo projeto Java Web, com o nome de “EquacaoSegundoGrau”
(sem as aspas), que deve ter um formulário usado para enviar os coeficientes de uma
equação de segundo grau para um Servlet, que por sua vez deve calcular as raízes
da equação em questão e imprimir essas raízes na saída padrão. As raízes de uma
equação do segundo grau podem ser determinadas usando a fórmula de Bhaskara
<http://pt.wikipedia.org/wiki/Bhaskara_II>. O Servlet deve verificar também se
os coeficientes passados representam uma equação do segundo grau válida. Esse
formulário deve usar o método GET.
CAPÍTULO
3
Expression Language E TagLibs
Edward Bulwer-Lytton
N
ESTE Capítulo teremos como objetivo entender a sintaxe, o propósito e
como utilizar tanto a Expression Language quanto as tags JSP, além de
aprendermos a lidar com as tags disponibilizadas na JavaServer Pages
Standard Tag Library (JSTL).
3.1 Introdução
Neste Capítulo iremos aprender duas funcionalidades muito importantes e úteis do
mundo Java Web: a Expression Language (EL) e as TagLibs. Essas duas funcionalidades
nos ajudarão na tarefa de não misturar código Java nas nossas JSPs. Você se lembra
quando falei que era possível inserir código Java dentro das JSPs, não lembra? Falei
também que isso não deveria ser feito, pois deixa o código difícil de ler e de manter,
além de fazer com que o trabalho do Web designer (que normalmente conhece mais
HTML e CSS) se torne difícil, visto que ele teria que ter um bom conhecimento em
Java e em como as JSPs funcionam. Usando a EL e as TagLibs, a manipulação de
dados, provenientes dos Servlets, se torna muito mais fácil, pois utiliza uma sintaxe
43
44 CAPÍTULO 3. EXPRESSION LANGUAGE E TAGLIBS
simples e fácil de entender, ajudando no trabalho de quem não conhece muito bem o
funcionamento de aplicações Web em Java. Vamos começar?
1 <!DOCTYPE html>
2
3 <html>
4 <head>
5 <title>Usando EL e TagLibs</title>
6 <meta charset="UTF-8">
7 <meta name="viewport"
8 content="width=device-width, initial-scale=1.0">
9
10 <style>
11 .alinharDireita {
12 text-align: right;
13 }
14 </style>
15
16 </head>
17 <body>
18 <div>
19
20 <h1>Dados do Produto</h1>
21
22 <form method="post" action="processaDadosProduto">
3.2. EXPRESSION LANGUAGE (EL) 45
23
24 <table>
25 <tr>
26 <td class="alinharDireita">Código:</td>
27 <td><input type="text" name="codigo"/></td>
28 </tr>
29 <tr>
30 <td class="alinharDireita">Descrição:</td>
31 <td><input type="text" name="descricao"/></td>
32 </tr>
33 <tr>
34 <td class="alinharDireita">Unidade de Medida:</td>
35 <td>
36 <select name="unidade">
37 <option value="kg">Quilograma</option>
38 <option value="l">Litro</option>
39 <option value="un">Unidade</option>
40 </select>
41 </td>
42 </tr>
43 <tr>
44 <td class="alinharDireita">Quant. em Estoque:</td>
45 <td><input type="text" name="quantidade"/></td>
46 </tr>
47 <tr>
48 <td class="alinharDireita" colspan="2">
49 <input type="submit" value="Enviar Dados"/>
50 </td>
51 </tr>
52 </table>
53
54 </form>
55
56 </div>
57 </body>
58 </html>
Note que nesse arquivo temos algumas coisas novas. A primeira novidade é a tag
<style> na linha 10. Usamos essa tag para criar regras de estilo para formatar/estilizar
a visualização do nosso nosso documento. Tudo que usarmos de formatação como
alinhamento, cor de texto etc., será definido usando estilos. Esses estilos são codi-
ficados usando as folhas de estilo Cascading Style Sheets (CSS). A sintaxe é muito
46 CAPÍTULO 3. EXPRESSION LANGUAGE E TAGLIBS
1
http://localhost:8088/UsandoELeTagLibs/processaDadosProduto
2
http://localhost:8088/UsandoELeTagLibs/index.html
3.2. EXPRESSION LANGUAGE (EL) 47
1 package entidades;
2
3 /**
4 * Entidade Produto.
5 *
6 * @author Prof. Dr. David Buzatto
7 */
8 public class Produto {
9
10 private int codigo;
11 private String descricao;
12 private String unidadeMedida;
13 private int quantidade;
14
15 public int getCodigo() {
16 return codigo;
17 }
18
19 public void setCodigo( int codigo ) {
20 this.codigo = codigo;
21 }
22
23 public String getDescricao() {
24 return descricao;
25 }
26
48 CAPÍTULO 3. EXPRESSION LANGUAGE E TAGLIBS
Com a classe Produto implementada, agora podemos criar objetos do tipo Produto
que vão conter os dados obtidos no formulário. Quem obtém e processa os dados
enviados pelo formulário são os Servlets, então vamos criar um. Crie o pacote “servlets”
-no mesmo nível do pacote “entidades” que já foi criado- para conter o Servlet que será
criado. A classe do nosso Servlet, que deverá estar dentro do pacote recém-criado, terá
o nome de “ProcessaDadosProdutoServlet” e deverá ser mapeada para “/processaDa-
dosProduto” em URL Pattern(s): . A implementação do método processRequest do
Servlet criado pode ser vista na Listagem 3.3.
1 package servlets;
2
3 import entidades.Produto;
4 import java.io.IOException;
5 import jakarta.servlet.RequestDispatcher;
6 import jakarta.servlet.ServletException;
7 import jakarta.servlet.annotation.WebServlet;
3.2. EXPRESSION LANGUAGE (EL) 49
8 import jakarta.servlet.http.HttpServlet;
9 import jakarta.servlet.http.HttpServletRequest;
10 import jakarta.servlet.http.HttpServletResponse;
11
12 /**
13 * Servlet para processamento de dados de produtos.
14 *
15 * @author Prof. Dr. David Buzatto
16 */
17 @WebServlet( name = "ProcessaDadosProdutoServlet",
18 urlPatterns = { "/processaDadosProduto" } )
19 public class ProcessaDadosProdutoServlet extends HttpServlet {
20
21 protected void processRequest(
22 HttpServletRequest request,
23 HttpServletResponse response )
24 throws ServletException, IOException {
25
26 request.setCharacterEncoding( "UTF-8" );
27
28 // obtém os dados do formulário
29 int codigo = 0;
30 int quantidade = 0;
31 String descricao = request.getParameter( "descricao" );
32 String unidadeMedida = request.getParameter( "unidade" );
33
34 try {
35 codigo = Integer.parseInt( request.getParameter( "codigo" ));
36 } catch ( NumberFormatException exc ) {
37 System.out.println( "Erro ao converter o codigo." );
38 }
39
40 try {
41 quantidade = Integer.parseInt( request.getParameter( "quantidade"
,→ ) );
42 } catch ( NumberFormatException exc ) {
43 System.out.println( "Erro ao converter a quantidade." );
44 }
45
46 // cria um novo produto e configura suas propriedades
47 // usando os dados obtidos do formulário
48 Produto prod = new Produto();
50 CAPÍTULO 3. EXPRESSION LANGUAGE E TAGLIBS
49 prod.setCodigo( codigo );
50 prod.setDescricao( descricao );
51 prod.setUnidadeMedida( unidadeMedida );
52 prod.setQuantidade( quantidade );
53
54 // configura um atributo no request chamado "produtoObtido"
55 // sendo que o valor do atributo é o objeto "prod"
56 request.setAttribute( "produtoObtido", prod );
57
58 // prepara um RequestDispatcher para direcionar para a página
59 // "exibeDados.jsp" que está no mesmo diretório em relação
60 // ao mapeamento deste Servlet
61 RequestDispatcher disp = request.getRequestDispatcher(
,→ "exibeDados.jsp" );
62
63 // faz o direcionamento, chamando o método forward.
64 disp.forward( request, response );
65
66 }
67
68 @Override
69 protected void doGet(
70 HttpServletRequest request,
71 HttpServletResponse response )
72 throws ServletException, IOException {
73 processRequest( request, response );
74 }
75
76 @Override
77 protected void doPost(
78 HttpServletRequest request,
79 HttpServletResponse response )
80 throws ServletException, IOException {
81 processRequest( request, response );
82 }
83
84 @Override
85 public String getServletInfo() {
86 return "ProcessaDadosProdutoServlet";
87 }
88
89 }
3.2. EXPRESSION LANGUAGE (EL) 51
request com esse atributo, ou seja, o objeto “prod”, que é um Produto, vai ficar
acessível a outro componente da nossa aplicação! Confuso? Já você vai entender, fique
calmo.
Na linha 61 criamos um RequestDispatcher, que é usado para direcionar o fluxo de
execução do Servlet que está sendo executado para um outro recurso. No caso, esse
recurso foi definido como exibeDados.jsp, uma página JSP que ainda vamos criar e
que vai usar o atributo “produtoObtido” configurado no request para exibir os dados
do produto.
Por fim, na linha 64, o método forward de disp, que é o nosso RequestDispatcher
para o recurso exibeDados.jsp é invocado, passando como parâmetro o request e
o response do Servlet. Quando o método forward é invocado, o servidor direciona o
fluxo para o recurso configurado e devolve o controle para o navegador caso o recurso
deva ser exibido por ele. Uma JSP, por padrão, é usada para isso não é mesmo?
O que a página exibeDados.jsp vai fazer é pegar o atributo “produtoObtido” configu-
rado no request e mostrar seus dados. Vamos criar então essa página. No NetBeans,
procure pela pasta chamada Web Pages . Clique com o botão direito nela, escolha
New e procure por JSP... . Se não achar essa opção, você já deve saber como proce-
der não é mesmo? Preencha o campo File Name: com “exibeDados” (sem as aspas)
e clique em Finish . O arquivo será criado e exibido no editor. Veja na Listagem 3.4
como ficou o código depois de ser editado.
17 <body>
18 <div>
19
20 <h1>Produto Obtido</h1>
21
22 <table>
23 <tr>
24 <td class="alinharDireita">Código:</td>
25 <td>${requestScope.produtoObtido.codigo}</td>
26 </tr>
27 <tr>
28 <td class="alinharDireita">Descrição:</td>
29 <td>${requestScope.produtoObtido.descricao}</td>
30 </tr>
31 <tr>
32 <td class="alinharDireita">Unidade de Medida:</td>
33 <td>${requestScope.produtoObtido.unidadeMedida}</td>
34 </tr>
35 <tr>
36 <td class="alinharDireita">Quant. em Estoque:</td>
37 <td>${requestScope.produtoObtido.quantidade}</td>
38 </tr>
39 <tr>
40 <td colspan="2">
41 <a href="index.html">Voltar</a>
42 </td>
43 </tr>
44 </table>
45
46 </div>
47 </body>
48 </html>
Copiou? Salvou? Faça um teste então! Execute o projeto, preencha o formulário e clique
em “Enviar Dados”. O Servlet será invocado, processará os dados e vai redirecionar
para a página exibeDados.jsp, que por sua vez vai mostrar os dados do produto.
Legal não é? Mágica? Não! Vamos entender o que está acontecendo no código do
arquivo exibeDados.jsp. Veja as linhas 25, 29, 33 e 37. A construção ${...} é a EL!
Usando a EL, nós podemos acessar valores que estão configurados no request e em
outros escopos também, que vamos aprender depois. No caso, o objeto requestScope
da EL faz referência ao objeto request do Servlet que é gerado a partir da JSP. Lembre-
se que uma JSP é convertida em um Servlet automaticamente pelo servidor!
54 CAPÍTULO 3. EXPRESSION LANGUAGE E TAGLIBS
13 <%--
14 Criando um objeto do tipo produto
15 usando a tag <jsp:useBean>
16 --%>
17 <jsp:useBean id="meuProduto"
18 class="entidades.Produto"
19 scope="page"/>
20 <jsp:setProperty name="meuProduto"
21 property="codigo"
22 value="4"/>
23 <jsp:setProperty name="meuProduto"
24 property="descricao"
25 value="Arroz"/>
26 <jsp:setProperty name="meuProduto"
27 property="unidadeMedida"
28 value="kg"/>
29 <jsp:setProperty name="meuProduto"
30 property="quantidade"
31 value="100"/>
32
33 <h1>Produto Criado:</h1>
34 ${pageScope.meuProduto.codigo},
35 ${pageScope.meuProduto.descricao},
36 ${pageScope.meuProduto.unidadeMedida},
37 ${pageScope.meuProduto.quantidade}
38
39 </body>
40 </html>
objeto chamado “meuProduto” que foi criado usando a tag <jsp:useBean> . Entre
as linhas 20 e 22, referenciamos o objeto “meuProduto” e configuramos a propriedade
“codigo” ((property="codigo") com o valor “4”. A instrução em Java equivalente a
estas duas linhas é meuProduto.setCodigo( 4 ) . A partir da linha 33, mostramos
então os dados do objeto “meuProduto” que foi criado usando EL. Note que desta vez,
usamos pageScope ao invés de requestScope, visto que o objeto existe apenas no
escopo da página (veja na linha 19).
Da mesma forma que existem as tags JSP padrão, você pode criar suas próprias tags
que podem ter comportamentos dos mais variados possíveis, entretanto nós não
iremos aprender a fazer isso. Como é possível criar tags personalizadas, nós podemos
usar conjuntos de tags que são implementadas por terceiros em nossos projetos.
Um desses conjuntos é a JavaServer Pages Standard Tag Library (JSTL), que vamos
aprender na próxima Seção. Vamos lá então!
11
12 <style>
13 .linhaPar {
14 background: #00bbee;
15 }
16
17 .linhaImpar {
18 background: #eeeeee;
19 }
20 </style>
21
22 </head>
23 <body>
24 <div>
25 <table>
26 <c:forEach begin="1" end="10" varStatus="i">
27 <c:choose>
28 <c:when test="${i.count % 2 == 0}">
29 <tr class="linhaPar">
30 <td>Linha ${i.count} JSTL é animal!</td>
31 </tr>
32 </c:when>
33 <c:otherwise>
34 <tr class="linhaImpar">
35 <td>Linha ${i.count} JSTL é show!</td>
36 </tr>
37 </c:otherwise>
38 </c:choose>
39 </c:forEach>
40 </table>
41 </div>
42 </body>
43 </html>
Copiou? Testou? Se tudo deu certo, você deve ter visto uma tabela zebrada (cor sim/cor
não). Veja a Figura 3.1.
58 CAPÍTULO 3. EXPRESSION LANGUAGE E TAGLIBS
Vamos analisar o código da Listagem 3.6. Talvez você tenha se assustado, mas não
se preocupe, estou aqui para te explicar. Vamos começar pela primeira linha. Nessa
linha, como em todos os JSPs que criamos até agora, usamos a diretiva page. As
diretivas nos JSPs são delimitadas por <%@ e %> e são usadas para realizar algumas
configurações no Servlet que será gerado a partir do JSPs. A diretiva page, no nosso
caso, é usada para configurar o response do Servlet, informando que ele vai conter
um documento do tipo “text/html” (atributo contentType) e que o encoding utilizado
(como os caracteres são codificados) é o UTF-8 (atributo pageEncoding). Veja que
isso é análogo ao que estamos fazendo manualmente na primeira linha do método
processRequest(...) dos nossos Servlets.
Na linha 2 utilizamos a diretiva taglib. Essa diretiva vai permitir que nós digamos
qual TagLib queremos utilizar. O atributo uri é usado para informarmos qual bibli-
oteca de tags queremos utilizar. No nosso caso, queremos utilizar as funcionalida-
des principais da JSTL, que são chamadas de “core”. A Uniform Resource Identifier
(URI) para definir isso é a http://java.sun.com/jsp/jstl/core. O outro atributo,
prefix (prefixo), nos permite definir um prefixo para usar as tags. O prefixo padrão
para a parte “core” da JSTL é “c”, mas podemos usar o prefixo que quisermos. Para
3.4. JAVASERVER PAGES STANDARD TAG LIBRARY - JSTL 59
manter o padrão, iremos usar o “c” mesmo. Assim, quando qualquer desenvolvedor
Web que conheça a JSTL bater o olho no código e ver alguma tag que inicie com “c:”
vai saber que a tag que está sendo utilizada faz parte do core da JSTL.
Entre as linhas 12 e 20 definimos duas classes CSS. Sendo que uma usaremos para
colorir o fundo das linhas pares de uma tabela, enquanto a outra será usada para
colorir o fundo das linhas ímpares. A propriedade usada em ambas as classes é a
background (fundo), sendo que em cada uma usamos uma cor diferente usando a
notação Red Green Blue (RGB) em hexadecimal. Nas linhas 25 e 40 delimitamos uma
tabela.
| Saiba Mais
Nunca ouviu falar de cores na notação em hexadecimal? De uma olhada nesses
links: <http://dematte.at/colorPicker/>, <http://paletton.com/>, <https:
//pt.wikipedia.org/wiki/Tripleto_hexadecimal> e <http://en.wikipedia.org/wik
i/Web_colors>
Na linha 26, usamos a tag <c:forEach> (olhe o prefixo!), usada para iterar um
determinado número de vezes ou sobre alguma lista de objetos. No nosso caso, quere-
mos que o que está entre <c:forEach> e </c:forEach> seja executado dez vezes,
pois definimos que a iteração deve iniciar em 1, usando o atributo begin, e ir até 10,
usando o atributo end. Queremos também que o status da iteração seja armazenado
na variável i, definida no atributo varStatus. Então temos um for que vai executar
dez vezes. Durante estas dez iterações, vamos construir nossa tabela, inserindo linhas
nela com apenas uma coluna, mas queremos que as linhas pares sejam coloridas
usando a classe .linhaPar , enquanto que as linhas ímpares sejam coloridas usando
a classe .linhaImpar . Sabemos que todo número par tem resto igual à zero numa
divisão por dois, correto? Então precisamos saber em qual iteração estamos, calcular o
resto e verificar se é zero. Se for, cria uma linha da tabela usando a classe .linhaPar ,
caso contrário, usa .linhaImpar .
Para criar séries de testes lógicos como numa estrutura if/else, nós usamos a tag
<c:choose> (to choose = escolher) e dentro dela colocamos as condições que que-
remos testar usando a tag <c:when> que é equivalente aos if’s e else if’s e,
por fim, se necessário, usamos a tag <c:otherwise> que é equivalente ao else .
Vamos analisar o código então: entre as linhas 27 e 38 nós definimos nossa estrutura
condicional usando a tag <c:choose> . Dentro dela, definimos na linha 28 uma tag
<c:when> , que testa (atributo test) se a divisão da propriedade count da variável
i por dois é igual a zero (par). Note o uso da EL e que podemos executar operações
60 CAPÍTULO 3. EXPRESSION LANGUAGE E TAGLIBS
aritméticas dentro dela! Se o resultado for true , o número é par e o conteúdo deste
<c:when> é gerado, ou seja, uma linha da tabela usando a classe .linhaPar . Caso
contrário, como não temos mais nenhum <c:when> , é gerado o código dentro do
<c:otherwise> , que por sua vez também gera uma linha da tabela, só que usando
a classe .linhaImpar . Fique à vontade para mudar o conteúdo gerado dentro de
cada linha, bem como as classes.
Viu como não é tão complicado? Na verdade é bem simples e fácil de usar, mas
precisamos praticar para ficarmos craques. Depois dessa introdução à EL e a JSTL,
nós já estamos quase prontos para começarmos o nosso primeiro projeto Web de
verdade, mas ainda temos que formalizar algumas coisas que serão vistas no próximo
Capítulo 4. Novamente, não se esqueça de praticar o que fizemos até agora.
3.5 Resumo
Neste Capítulo nós aprendemos a criar um formulário que teve seus dados tratados
por um Servlet, que por sua vez redirecionou o fluxo da aplicação para outra página
JSP, utilizada para mostrar os dados informados no formulário. Com isso, tivemos uma
noção do que é a EL e como ela funciona. Depois aprendemos um pouco sobre as
tags padrão da especificação dos JSPs e, por fim, aprendemos utilizar a JSTL em nosso
projeto, além de fazermos alguns testes. No Capítulo 4 vamos dar uma paradinha
com os JSPs e Servlets para podermos aprender como estruturar um projeto Web que
trabalha com banco de dados. Tenho certeza que você vai gostar bastante.
3.6 Exercícios
Exercício 3.2: Justifique porque é melhor usar a JSTL ou qualquer outra TagLib ao
invés de usar código Java diretamente nas JSPs.
3.7 Projetos
Projeto 3.1: Modifique o Projeto 2.1 do Capítulo 2 para executar da mesma forma que
o projeto criado neste Capítulo, ou seja, o formulário deve submeter os dados para
um Servlet, que por sua vez deve criar e enviar um objeto através do request para um
JSP, que deve exibir ao usuário usando EL.
3.7. PROJETOS 61
Projeto 3.2: Modifique o Projeto 2.2 do Capítulo 2 para executar da mesma forma que
o projeto criado neste Capítulo, ou seja, o formulário deve submeter os dados para
um Servlet, que por sua vez deve criar e enviar um objeto através do request para um
JSP, que deve exibir ao usuário usando EL.
Projeto 3.3: Você já sabe que uma JSP na verdade é um Servlet não é mesmo? Será
então que podemos definir na action de um formulário o endereço de um arquivo
JSP ao invés de um Servlet? Crie um novo projeto Java Web, chamado “JSPTrataFormu-
lario”, onde você deve ter um formulário no index.html que contenha dois campos:
nome e idade. A action deste formulário deve apontar para um arquivo JSP chamado
“exibeDadosForm.jsp” (sem as aspas). Nesse arquivo, você deve mostrar os dados rece-
bidos do formulário do index.html usando EL. Dica: para acessar os parâmetros do
request usando EL, usa-se ${param.nomeDoParametro}. Por exemplo, o parâmetro
idade é acessado usando ${param.idade}.
Projeto 3.4: Crie um novo projeto Java Web, com o nome de “TabelaArbitraria”,
onde no index.html você deve ter um formulário que pede ao usuário que seja di-
gita a quantidade de linhas e de colunas que ele deseja que uma tabela seja gerada.
Aponte a action deste formulário para outro arquivo JSP, chamado “montadorTa-
bela.jsp”, que obtém os dados enviados pelo formulário do index.html usando EL e
usa dois <c:forEach> aninhados para construir a tabela de dimensões arbitrárias.
Monte a tabela somente se o tamanho de linhas e de colunas for maior que zero.
Se não for, exiba uma mensagem ao usuário. Dica: para testar se duas condições
são verdadeiras, ou seja, se param.colunas > 0 e param.linhas > 0, use o opera-
dor and (e) da EL, enquanto o operador “maior que” é o gt (greater than). Ou seja,
test="${(param.linhas gt 0) and (param.colunas gt 0)}".
CAPÍTULO
4
PADRÕES DE P ROJETO : Factory, DAO E
MVC
Molière
N
ESTE Capítulo teremos como objetivo entender e aplicar os Padrões de
Projeto Factory, DAO e MVC. Além disso iremos aprender a integrar o
acesso à banco de dados em uma aplicação Java.
4.1 Introdução
A partir de agora iremos dar um passo importantíssimo em nossos estudos sobre
desenvolvimento de software, pois iremos aprender a lidar com um banco de dados em
um sistema de testes que iremos construir. Isso nos dará o embasamento necessário
para que no Capítulo 5 nós consigamos fazer essa mesma integração em aplicações
Web. Além de aprendermos a conectar nossa aplicação com uma base de dados,
iremos aprender também alguns padrões de projeto que nos ajudarão a organizar
nossa aplicação de forma a melhorar sua manutenção.
Antes de começarmos a discussão e a implementação desses padrões, nós preci-
samos preparar nosso ambiente de desenvolvimento. O NetBeans já temos insta-
63
64 CAPÍTULO 4. PADRÕES DE PROJETO: FACTORY, DAO E MVC
lado. O Sistema Gerenciador de Banco de Dados (SGBD) que iremos utilizar, o Mari-
aDB1 /MySQL, você provavelmente já deve ter instalado também. Com isso pronto,
precisamos instalar uma ferramenta para nos ajudar a criar nossa base de dados.
Iremos utilizar o MySQL Workbench, uma ferramenta gratuita para gerenciamento do
MariaDB/MySQL.
1
Provavelmente você deve ter instalado na sua máquina o MariaDB que é distribuído junto ao
XAMPP
4.2. PREPARANDO O AMBIENTE 65
conexão será bem sucedida, sendo avisada através de um diálogo com a mensagem
“Successfully made the MySQL connection”. Lembre-se que o MariaDB/MySQL deve
estar em execução! Por fim, clique no botão OK , o diálogo será fechado e a conexão
aparecerá.
Clique duas vezes na conexão criada. Novamente, será mostrado um aviso, dizendo
sobre a incompatibilidade de protocolos. Não marque a opção “Don’t show this mes-
sage again” e clique em Continue Anyway . Pronto? Muito bem! Sempre que abrir-
mos o Workbench, essas configurações já estarão feitas, não se preocupe. Agora nós
vamos criar uma base de dados para trabalharmos nos exemplos deste Capítulo.
Na interface que se abriu, do lado esquerdo, em Navigator há duas abas que ficam
abaixo. Uma é chamada Administration , que é a que está selecionada por padrão,
e outra chamada Schemas . Clique em Schemas . No aba de esquemas, na parte
em branco, clique com o botão direito e escolha Create Schema . Preencha o campo
Name: com “testes_padroes” (sem as aspas) e deixe o campo Charset/Collation:
como “Default Charset” e “Default Collation”. Clique em Apply . Um diálogo será
aberto para mostrar o código SQL que será executado. Clique em Apply e se der tudo
certo, clique em Finish . A aba de criação de esquemas continuará aberta, podendo
ser fechada. Ao terminar esse processo, o novo esquema (vamos chamar os esquemas
de base de dados a partir de agora) será criado e estará listado na lista SCHEMAS .
Vamos configurar a base de dados que acabamos de criar como padrão, ou seja, as
instruções SQL que realizarmos serão aplicados nessa base. Para isso, clique com o
botão direito em testes_padroes e escolha a opção Set as Default Schema . Veja a
Figura 4.2. Após fazer isso, o nome da base de dados ficará em negrito.
4.2. PREPARANDO O AMBIENTE 67
Feito isso, clique em Apply . Um diálogo será aberto para exibir o código SQL que será
executado (Listagem 4.1). Clique em Apply e depois em Finish . Pronto, criamos a
nossa tabela para armazenar dados dos países, ela poderá ser vista do lado esquerdo,
na aba Schemas , dentro da base testes_padroes. Deixe o Workbench aberto e abra
o NetBeans.
ser implementada pelos fabricantes dos SGBDs, permitindo assim que os programas
feitos em Java possam se comunicar com estes SGBDs. Normalmente, esses pacotes
que implementam o JDBC são chamados de “Drivers JDBC” e são obtidos nos sites
dos fabricantes dos SGBDs.
A questão agora é: “Como conectar no banco de dados?”. Para que possamos conectar
no MariaDB, existem uma série de “comandos” que precisamos fazer cada vez que
queremos estabelecer uma conexão. Você, como desenvolvedor, sabe que uma boa
prática de programação é encapsular trechos de código que fazem uma determinada
tarefa em funções e que as funções em Java são chamadas de métodos.
Legal, mas e o que o tal do “padrão de projeto” tem haver com isso? Ou melhor, o que é
um padrão de projeto? O termo padrão de projeto (design pattern em inglês) foi criado
por Christopher Alexander (ALEXANDER; ISHIKAWA; SILVERSTEIN, 1977) na década
de 1970 para designar soluções de sucesso para problemas recorrentes na área da
arquitetura. Alexander definiu nessa época uma série de padrões que apresentavam
soluções padrão para problemas que aconteciam de forma corriqueira, sendo que
uma série de padrões correlatos foram o que podemos chamar de linguagem de
padrões. A partir do termo cunhado por Alexander, alguns programadores começaram
a criar padrões de projeto para a computação, iniciando esse trabalho do domínio da
programação orientada a objetos.
O livro “Padrões de Projeto: Soluções Reutilizáveis de Software Orientado a Objetos”
(GAMMA et al., 2000) é a referência básica para os padrões de projeto criados para
resolver problemas relacionados ao desenvolvimento orientado a objetos. A partir de
agora, quando você ler “padrão de projeto”, entenda que estarei falando de padrões
relacionados ao desenvolvimento de software orientado a objetos, tudo bem? Sendo
assim, o primeiro padrão que iremos aprender se chama Factory (fábrica).
Vamos entender o contexto do padrão. Imagine que no seu programa você precisa
criar e utilizar um determinado tipo de objeto muitas e muitas vezes e que este
objeto precisa ser inicializado com uma série de valores, sendo que esses valores
normalmente são sempre os mesmos. Assim, cada vez que você instância esse objeto,
você precisa executar uma determinada quantidade de código. Como resolver isso?
Criar um método que faça essa tarefa é uma boa solução não é mesmo? Mas em
qual classe eu vou escrever esse método? No padrão Factory, nós criamos classes
especializadas que serão fábricas de objetos e que terão um método que executará a
tarefa da fábrica, ou seja, criar um determinado tipo de objeto.
No nosso projeto, um tipo de objeto que usaremos muito, é um objeto que representa a
conexão entre nosso programa escrito em Java e o SGBD. Sendo assim, nós precisamos
de uma fábrica de conexões! Vamos implementar a fábrica? No projeto que você criou
agora há pouco, o NetBeans gerou por padrão um pacote chamado “padroesempratica”
4.3. PADRÃO DE PROJETO FACTORY 71
dentro da pasta Source Packages . Crie dentro deste pacote outro com o nome de
“jdbc” (sem as aspas) e dentro do pacote “padroesempratica.jdbc”, crie uma classe Java
com o nome de “ConnectionFactory” (sem as aspas). Essa classe conterá o método
que vai fabricar a conexão. Veja o código dela na ListagemListagem 4.2.
1 package padroesempratica.jdbc;
2
3 import java.sql.Connection;
4 import java.sql.DriverManager;
5 import java.sql.SQLException;
6
7 /**
8 * Uma fábrica de conexões.
9 *
10 * @author Prof. Dr. David Buzatto
11 */
12 public class ConnectionFactory {
13
14 /**
15 * O método getConnection retorna uma conexão com a base de dados
16 * testes_padroes.
17 *
18 * @return Uma conexão com o banco de dados testes_padroes.
19 * @throws SQLException Caso ocorra algum problema durante a conexão.
20 */
21 public static Connection getConnection() throws SQLException {
22
23 /* O método getConnection de DriverManagaer recebe como parâmetro
24 * a URL da base de dados, o usuário usado para conectar na base
25 * e a senha deste usuário. O Driver JDBC apropriado será
26 * carregado com base na biblioteca configurada.
27 */
28 return DriverManager.getConnection(
29 "jdbc:mariadb://localhost/testes_padroes",
30 "root",
31 "" );
32
33 }
34
72 CAPÍTULO 4. PADRÕES DE PROJETO: FACTORY, DAO E MVC
35 }
Copiou o código? Ótimo! Esta classe tem um método estático chamado getConnection()
que vai fabricar a conexão para nós e que caso ocorra algum problema, vai lançar
uma exceção do tipo SQLException. Toda vez que chamarmos esse método, ele vai
retornar uma nova conexão para nós e o Driver JDBC apropriado será carregado
automaticamente pelo DriverManager.
Agora precisamos testar esse método para ver se não está havendo nenhum erro.
Clique novamente com o botão direito no pacote “padroesempratica” e crie um novo
pacote chamado “testes”. Dentro do pacote “padroesempratica.testes”, crie uma classe
chamada “TesteConnectionFactory”. Como você deve saber, para que uma classe
em Java possa ser executada, nós precisamos implementar o método main(...)
com uma determinada assinatura. O que vamos fazer no método main(...) da
classe “TesteConnectionFactory” é tentar criar uma conexão e ver se nenhum erro é
retornado. Na Listagem 4.3 você pode ver o código desta classe.
1 package padroesempratica.testes;
2
3 import java.sql.Connection;
4 import java.sql.SQLException;
5 import padroesempratica.jdbc.ConnectionFactory;
6
7 /**
8 * Teste de conexão.
9 *
10 * @author Prof. Dr. David Buzatto
11 */
12 public class TesteConnectionFactory {
13
14 public static void main( String[] args ) {
15
16 // tenta criar uma conexão
17 try {
18
19 Connection conexao = ConnectionFactory.getConnection();
20 System.out.println( "Conexão criada com sucesso!" );
21
4.4. PADRÃO DE PROJETO DATA ACCESS OBJECT (DAO) 73
Copie o código para a classe e salve o arquivo. Para executarmos apenas uma classe
que tem o método main(...) , basta clicar com o botão direito no editor e escolhe a
opção Run File , ou então, com o arquivo aberto no editor, usar o atalho <Shift+F6>.
Fazendo isso, a classe vai ser compilada e executada pelo NetBeans. Se tudo esti-
ver correto, você verá na saída a mensagem “Conexão criada com sucesso!”. Deu
erro? Verifique se o código da classe ConnectionFactory está correto e se a senha
do usuário root, definida dentro do método getConnection() está correta. No
nosso caso, ela é vazia. Agora que está tudo certo, vamos simular um erro. Entre na
classe ConnectionFactory e coloque uma senha inválida (terceiro parâmetro do mé-
todo getConnection(...) de DriverManagaer) para o usuário root, por exemplo,
“123”. Volte na classe de testes e execute-a novamente (<Shift+F6>). Gerou um erro
não foi? A mensagem “Erro ao tentar criar a conexão!” deve ter sido exibida, seguida
de várias linhas que explicam o erro ocorrido. A primeira linha dos erros diz que o
acesso foi negado para o usuário “root@localhost” não foi? Por que aconteceu isso?
Porque a senha está errada! Volte na fábrica de conexões e coloque a senha correta
(vazia). Teste novamente. Agora deve estar tudo certo.
Legal, temos uma fábrica de conexões, mas do que adianta uma fábrica de alguma
coisa se a gente não usar o que é fabricado? Vamos para o próximo padrão, onde
iremos organizar uma camada de persistência para nossa aplicação e usaremos a
fábrica de conexões para viabilizar a comunicação entre nossa aplicação e o SGBD.
ensinados a inserir todo o código SQL para fazer isso, sendo que esse código é im-
plementado usando uma instrução INSERT . O mesmo aconteceria para os botões
alterar e excluir. Será que essa é a melhor solução? Será que a classe que implementa
nossa interface gráfica tem que ter essa responsabilidade, ou seja, lidar com SQL? E se
por algum motivo nós quiséssemos usar o código para criar um novo país em alguma
outra janela? Teríamos que copiar o código de inserção novamente? Tenho certeza
que para essa última pergunta você já deve ter respondido mentalmente que “NÃO!”,
pois podemos criar métodos que executariam essa operação, mas então te pergunto:
Como fazer isso?
Para resolver esse problema, existe um padrão de projeto onde a ideia é isolar todo
acesso ao banco de dados em classes que seriam responsáveis em fazer a comunicação
entre a aplicação Java, ou qualquer aplicação escrita usando uma linguagem orientada
a objetos, e o banco de dados. Esse padrão se chama Data Access Object (DAO), sendo
este um dos mais famosos. Vamos aprender como implementá-lo?
Um objeto do tipo DAO deve ter a capacidade de executar as operações básicas sobre
uma determinada tabela de um banco de dados. Essas operações são comumente
chamadas de “CRUD” que vem de “Create, Read, Update e Delete” (Criar, Ler, Atualizar
e Excluir). Praticamente cada tabela do nosso banco de dados terá no lado da aplicação
uma classe implementada que representará um registro da tabela (tabela “pais”, classe
Pais) por meio de um objeto do tipo em questão, além de ter uma classe DAO que vai
manipular esses objetos. Como precisamos definir essas quatro operações básicas
que cada DAO vai conter, vamos criar uma classe abstrata que servirá de modelo.
No pacote “padroesempratica”, crie um novo pacote chamado “dao”. Dentro do pacote
“padroesempratica.dao”, crie uma classe chamada “DAO” e copie o código apresentado
na Listagem 4.4.
1 package padroesempratica.dao;
2
3 import java.sql.Connection;
4 import java.sql.SQLException;
5 import java.util.List;
6 import padroesempratica.jdbc.ConnectionFactory;
7
8 /**
9 * DAO genérico.
10 *
4.4. PADRÃO DE PROJETO DATA ACCESS OBJECT (DAO) 75
53 /**
54 * Método abstrato para salvar uma instância de uma
55 * entidade da base de dados.
56 *
57 * É o "C" do CRUD.
58 *
59 * @param obj Instância do objeto da entidade a ser salvo.
60 * @throws SQLException Caso ocorra algum erro durante a gravação.
61 */
62 public abstract void salvar( Tipo obj ) throws SQLException;
63
64 /**
65 * Método abstrato para atualizar uma instância de uma
66 * entidade da base de dados.
67 *
68 * É o "U" do CRUD.
69 *
70 * @param obj Instância do objeto da entidade a ser atualizado.
71 * @throws SQLException Caso ocorra algum erro durante a atualização.
72 */
73 public abstract void atualizar( Tipo obj ) throws SQLException;
74
75 /**
76 * Método abstrato para excluir uma instância de uma
77 * entidade da base de dados.
78 *
79 * É o "D" do CRUD.
80 *
81 * @param obj Instância do objeto da entidade a ser salvo.
82 * @throws SQLException Caso ocorra algum erro durante a exclusão.
83 */
84 public abstract void excluir( Tipo obj ) throws SQLException;
85
86 /**
87 * Método abstrato para obter todas as instâncias de uma
88 * entidade da base de dados.
89 *
90 * É o "R" do CRUD.
91 *
92 * @return Lista de todas as instâncias da entidade.
93 * @throws SQLException Caso ocorra algum erro durante a consulta.
94 */
4.4. PADRÃO DE PROJETO DATA ACCESS OBJECT (DAO) 77
Copiou o código? Ótimo! Vamos entendê-lo. Na linha 13 definimos uma classe abs-
trata2 chamada DAO que diz que toda classe que for implementá-la deve fornecer
um tipo genérico chamado de Tipo. As construções entre < e > são chamadas de
“Tipos Genéricos” em Java. Note que o tipo Tipo é usado em todo o corpo da classe.
Está confuso? Acalme-se, logo você vai entender.
Na linha 16 é declarada uma variável de instância que referenciará uma conexão,
que sempre será obtida usando o método getConnection() definido na linha 39.
Observe que poderíamos ter declarado essa conexão como protected , mas vamos
deixá-la como private e usar o método getConnection() para obtê-la. Na linha
49 é definido o método para fechar a conexão, afinal, sempre depois de usarmos uma
conexão, precisamos fechá-la.
Veja que no construtor da classe a conexão é obtida usando a fábrica que criamos na
Seção anterior! Esse construtor será executado quando instanciarmos os objetos das
classes que estenderem esse DAO, então, quando criarmos nossos objetos DAO, uma
conexão com o banco de dados será estabelecida.
A partir da linha 62 são definidos todos os métodos CRUD deste DAO genérico. Note
que temos dois métodos que correspondem à parte “R” do CRUD, onde um obtém
todas as entidades cadastradas e outro obtém apenas uma usando como base seu
identificador.
Com o DAO genérico pronto, vamos implementar a classe que vai representar a tabela
2
Classes abstratas não podem ser instanciadas, são usadas como modelos.
78 CAPÍTULO 4. PADRÕES DE PROJETO: FACTORY, DAO E MVC
1 package padroesempratica.entidades;
2
3 /**
4 * Classe Pais.
5 *
6 * @author Prof. Dr. David Buzatto
7 */
8 public class Pais {
9
10 private int id;
11 private String nome;
12 private String sigla;
13
14 }
Tenho certeza que você se lembra da discussão sobre o padrão JavaBeans não é
mesmo? Onde foi dito que devemos expor ao mundo “fora da classe” os atributos
que nós queremos que possam ser configurados e obtidos por seus utilizadores?
Da primeira vez que falamos sobre isso, nós implementamos manualmente cada
método set e get correspondente a um campo privado não foi? Como essa tarefa é
muito corriqueira, o NetBeans tem uma funcionalidade que faz isso automaticamente
para nós. Com a classe implementada, como exibido na Listagem 4.5, clique com o
botão direito no editor e escolha Insert Code... . Ao clicar nesta opção, uma pequena
lista com o nome de Generate será exibida no editor. Nessa lista, escolha a opção
Getter and Setter... . Adivinhe o que vamos fazer? Gerar os métodos get e set para
cada campo da classe! Fazendo isso, um diálogo será exibido, mostrando todos os
campos privados da classe. Marque cada um dos campos clicando na caixa de seleção
correspondente, ou então, a classe inteira e clique no botão Generate . Veja o que
4.4. PADRÃO DE PROJETO DATA ACCESS OBJECT (DAO) 79
aconteceu! A IDE gerou o código dos gets e sets para nós! Segue na Listagem 4.6 o
código completo da classe Pais.
1 package padroesempratica.entidades;
2
3 /**
4 * Classe Pais.
5 *
6 * @author Prof. Dr. David Buzatto
7 */
8 public class Pais {
9
10 private int id;
11 private String nome;
12 private String sigla;
13
14 public int getId() {
15 return id;
16 }
17
18 public void setId( int id ) {
19 this.id = id;
20 }
21
22 public String getNome() {
23 return nome;
24 }
25
26 public void setNome( String nome ) {
27 this.nome = nome;
28 }
29
30 public String getSigla() {
31 return sigla;
32 }
33
34 public void setSigla( String sigla ) {
35 this.sigla = sigla;
36 }
80 CAPÍTULO 4. PADRÕES DE PROJETO: FACTORY, DAO E MVC
37
38 }
Muito legal não é mesmo? Agora que temos a classe que representa a estrutura da ta-
bela “pais” da nossa base de dados, vamos implementar nosso primeiro DAO concreto,
ou seja, uma classe que vai estender a classe abstrata DAO. Novamente, no pacote
“padroesempratica.dao”, crie uma classe chamada “PaisDAO” (sem as aspas). Essa
classe irá lidar com os objetos do tipo Pais, fazendo a ponte entre nossos objetos e o
banco de dados. Com a classe criada, copie o código apresentado na Listagem 4.7.
1 package padroesempratica.dao;
2
3 import padroesempratica.entidades.Pais;
4
5 /**
6 * DAO para a classe Pais.
7 *
8 * @author Prof. Dr. David Buzatto
9 */
10 public class PaisDAO extends DAO<Pais> {
11
12 }
Veja que nosso PaisDAO vai estender DAO, informando como tipo a classe Pais (DAO<Pais>).
Ao copiar o código, você perceberá que o NetBeans vai reclamar, dizendo que tem
um erro na classe. O nome da classe ficará destacado em vermelho. Passe o mouse
por cima do nome e aguarde. Será exibida a causa do erro. No erro, é dito que a classe
PaisDAO não implementa todos os métodos abstratos da classe DAO e isso é verdade,
visto que como estamos estendendo a classe DAO, precisamos implementar todos os
métodos abstratos que foram definidos nela e ainda não fizemos isso. Teríamos então
que implementar manualmente todos os métodos marcados como abstratos na classe
DAO. Ao invés de fazermos isso manualmente, o NetBeans pode nos ajudar novamente.
Veja que na linha do erro, à esquerda, é mostrada uma pequena lâmpada com uma
bolinha vermelha. Clique nela. Ao clicar, o NetBeans vai listar as alternativas que ele
pode executar para resolver o erro. Veja a Figura 4.4.
4.4. PADRÃO DE PROJETO DATA ACCESS OBJECT (DAO) 81
Clique na opção Implement all abstract methods e, novamente, como num passe de
mágica, o NetBeans gera todo o esqueleto da classe para nós, criando uma imple-
mentação padrão para cada método abstrato da classe DAO. Mesmo ao fazer isso, o
NetBeans continua reclamando que existe um erro. Nesse caso, é dito que é lançada
uma exceção no construtor padrão3 . Você se lembra que lá no DAO genérico nós temos
um construtor que cria a conexão e que ele lança uma SQLException? Pois bem,
quando criamos um objeto de uma determinada classe, o construtor da superclasse
da classe em questão a primeira coisa que será executada. Como estendemos DAO
em PaisDAO, ao tentarmos instanciar um objeto do tipo PaisDAO, o construtor de
PaisDAO será executado, além do construtor de DAO, que é sua superclasse. Como o
construtor de DAO lança uma exceção caso ocorra algum problema, nós precisamos
ou tratar ou dizer que o construtor de PaisDAO também lança esse tipo de exceção.
Nós iremos usar a segunda abordagem. Para isso, basta implementar o construtor
padrão de PaisDAO e dizer que ele lança esse tipo de exceção. Sendo assim, segue na
Listagem 4.8 a implementação que temos até agora da classe PaisDAO.
3
O construtor padrão é o construtor que não tem nenhum parâmetro.
82 CAPÍTULO 4. PADRÕES DE PROJETO: FACTORY, DAO E MVC
1 package padroesempratica.dao;
2
3 import java.sql.SQLException;
4 import java.util.List;
5 import padroesempratica.entidades.Pais;
6
7 /**
8 * DAO para a classe Pais.
9 *
10 * @author Prof. Dr. David Buzatto
11 */
12 public class PaisDAO extends DAO<Pais> {
13
14 public PaisDAO() throws SQLException {
15 super();
16 }
17
18 @Override
19 public void salvar( Pais obj ) throws SQLException {
20 throw new UnsupportedOperationException( "Not supported yet." );
21 }
22
23 @Override
24 public void atualizar( Pais obj ) throws SQLException {
25 throw new UnsupportedOperationException( "Not supported yet." );
26 }
27
28 @Override
29 public void excluir( Pais obj ) throws SQLException {
30 throw new UnsupportedOperationException( "Not supported yet." );
31 }
32
33 @Override
34 public List<Pais> listarTodos() throws SQLException {
35 throw new UnsupportedOperationException( "Not supported yet." );
36 }
37
38 @Override
39 public Pais obterPorId( int id ) throws SQLException {
4.4. PADRÃO DE PROJETO DATA ACCESS OBJECT (DAO) 83
Muito bom! Até agora preparamos toda o esqueleto do nosso PaisDAO, mas SQL que é
bom, nada. Vamos agora implementar nosso primeiro método do CRUD, o “salvar”. An-
tes de implementar o método “salvar”, importe a classe java.sql.PreparedStatement.
Na Listagem 4.9 pode ser vista a implementação do método salvar da classe PaisDAO.
1 package padroesempratica.dao;
2
3 import java.sql.PreparedStatement;
4 import java.sql.SQLException;
5 import java.util.List;
6 import padroesempratica.entidades.Pais;
7
8 /**
9 * DAO para a classe Pais.
10 *
11 * @author Prof. Dr. David Buzatto
12 */
13 public class PaisDAO extends DAO<Pais> {
14
15 public PaisDAO() throws SQLException {
16 super();
17 }
18
19 @Override
20 public void salvar( Pais obj ) throws SQLException {
21
22 String sql = "INSERT INTO pais( nome, sigla ) " +
23 "VALUES( ?, ? );";
24
25 PreparedStatement stmt = getConnection().prepareStatement( sql );
26 stmt.setString( 1, obj.getNome() );
27 stmt.setString( 2, obj.getSigla() );
28
84 CAPÍTULO 4. PADRÕES DE PROJETO: FACTORY, DAO E MVC
29 stmt.executeUpdate();
30 stmt.close();
31
32 }
33
34 @Override
35 public void atualizar( Pais obj ) throws SQLException {
36 throw new UnsupportedOperationException( "Not supported yet." );
37 }
38
39 @Override
40 public void excluir( Pais obj ) throws SQLException {
41 throw new UnsupportedOperationException( "Not supported yet." );
42 }
43
44 @Override
45 public List<Pais> listarTodos() throws SQLException {
46 throw new UnsupportedOperationException( "Not supported yet." );
47 }
48
49 @Override
50 public Pais obterPorId( int id ) throws SQLException {
51 throw new UnsupportedOperationException( "Not supported yet." );
52 }
53
54 }
Vamos analisar o código para ver o que está acontecendo. Na linha 20 está definida a
assinatura do método. O método salvar recebe um parâmetro do tipo Pais, sendo
que os dados do objeto passado nesse parâmetro serão persistidos no banco de dados.
Nas linhas 22 e 23 é definida a instrução INSERT do banco de dados. Como a coluna
id da tabela pais foi definida como auto-incremento, nós não precisamos fornecê-la.
Ao invés de definirmos manualmente os valores das colunas nome e sigla, perceba
que utilizamos sinais de interrogação, indicando que no lugar de cada ponto de inter-
rogação será trocado pelo valor correto. Na linha 25 é criado um PreparedStatement
a partir da conexão do DAO usando o código SQL que foi definido nas linhas 22 e 23.
Na linha 26, o primeiro parâmetro do código SQL (primeiro ponto de interrogação)
é “trocado” pelo valor retornado pelo método getNome() do objeto referenciado
por obj que é do tipo Pais. A mesma coisa acontece na linha 27, onde o segundo
parâmetro do código SQL (segundo ponto de interrogação) é “trocado” pelo valor
retornado pelo método getSigla() . Com o PreparedStatement configurado, na
4.4. PADRÃO DE PROJETO DATA ACCESS OBJECT (DAO) 85
linha 29 mandamos que seja executado o PreparedStatement. Por fim, na linha 30,
fechamos o PreparedStatement. Fácil não é?
Vamos testar? No pacote “padroesempratica.testes”, crie uma classe chamada TestePaisDAO
e copie o código da Listagem 4.10.
1 package padroesempratica.testes;
2
3 import java.sql.SQLException;
4 import padroesempratica.dao.PaisDAO;
5 import padroesempratica.entidades.Pais;
6
7 /**
8 * Testes da classe PaisDAO.
9 *
10 * @author Prof. Dr. David Buzatto
11 */
12 public class TestePaisDAO {
13
14 public static void main( String[] args ) {
15
16 Pais pais = new Pais();
17 pais.setNome( "Brasil" );
18 pais.setSigla( "BR" );
19
20 PaisDAO dao = null;
21
22 try {
23
24 dao = new PaisDAO();
25 dao.salvar( pais );
26
27 } catch ( SQLException exc ) {
28 exc.printStackTrace();
29 } finally {
30
31 if ( dao != null ) {
32
33 try {
86 CAPÍTULO 4. PADRÕES DE PROJETO: FACTORY, DAO E MVC
34 dao.fecharConexao();
35 } catch ( SQLException exc ) {
36 System.err.println( "Erro ao fechar a conexão!" );
37 exc.printStackTrace();
38 }
39
40 }
41
42 }
43
44 }
45
46 }
Copiou o código? Execute a classe (botão direito no arquivo, Run File ou <Shift+F6>).
Se tudo estiver correto, a classe será compilada e executada e nenhum erro será emi-
tido. Fazendo isso, um novo registro na tabela “pais” será inserido. Vamos confirmar
isso? No MySQL Workbench deve haver um editor SQL já aberto. Se não houver, abra
um clicando no ícone com uma folha com a sigla SQL e um sinal de +. Confirme tam-
bém se a base de dados “testes_padroes” está configurado como padrão ou está ativa.
No editor, digite SELECT * FROM pais; e clique no botão que tem um desenho
de um raio na barra de ferramentas da aba do editor. O código SQL digitado será
executado e o resultado será exibido logo abaixo. Veja a Figura 4.5.
4.4. PADRÃO DE PROJETO DATA ACCESS OBJECT (DAO) 87
Muito bem! Nosso método salvar de PaisDAO está funcionando corretamente. Va-
mos analisar o código da Listagem 4.10. Entre as linhas 16 e 18 instanciamos e
configuramos um objeto do tipo Pais. O nome desse país é Brasil e a sigla é BR.
Na linha 20, declaramos uma referência do tipo PaisDAO que foi inicializada com
null . Como todo o código dos métodos do DAO podem lançar uma exceção do tipo
SQLException, temos que usar um bloco try . Na linha 24 instanciamos o PaisDAO
e na linha 25 passamos o objeto do tipo Pais que criamos para o método “salvar” do
DAO, que por sua vez vai executar o código SQL que definimos. Caso ocorra algum
problema durante a execução de uma dessas duas linhas, o catch que ouve exceções
do tipo SQLException captura o erro e manda mostrar esses problemas dando um
printStackTrace() no objeto que representa a exceção. Por fim, temos um bloco
finally que trata do fechamento da conexão. Sempre quando usarmos um DAO,
precisamos fechar sua conexão quando terminarmos de usar. Na linha 31 verifica-se
se o dao aponta para null . Se não apontar, tenta fechar a conexão, que também
pode lançar uma exceção do tipo SQLException e que é tratada dentro do bloco try
que está aninhado no finally .
88 CAPÍTULO 4. PADRÕES DE PROJETO: FACTORY, DAO E MVC
Agora que já criamos e testamos nosso primeiro método do PaisDAO, vamos imple-
mentar o restante dos métodos. Os métodos de pesquisa (“R” do CRUD) serão um
pouco diferente, sendo assim, eu os discutirei depois. Vamos lá, copie o restante do
código para que seu PaisDAO fique igual ao da Listagem 4.11.
1 package padroesempratica.dao;
2
3 import java.sql.PreparedStatement;
4 import java.sql.ResultSet;
5 import java.sql.SQLException;
6 import java.util.ArrayList;
7 import java.util.List;
8 import padroesempratica.entidades.Pais;
9
10 /**
11 * DAO para a classe Pais.
12 *
13 * @author Prof. Dr. David Buzatto
14 */
15 public class PaisDAO extends DAO<Pais> {
16
17 public PaisDAO() throws SQLException {
18 super();
19 }
20
21 @Override
22 public void salvar( Pais obj ) throws SQLException {
23
24 String sql = "INSERT INTO pais( nome, sigla ) " +
25 "VALUES( ?, ? );";
26
27 PreparedStatement stmt = getConnection().prepareStatement( sql );
28 stmt.setString( 1, obj.getNome() );
29 stmt.setString( 2, obj.getSigla() );
30
31 stmt.executeUpdate();
32 stmt.close();
33
34 }
4.4. PADRÃO DE PROJETO DATA ACCESS OBJECT (DAO) 89
35
36 @Override
37 public void atualizar( Pais obj ) throws SQLException {
38
39 String sql = "UPDATE pais " +
40 "SET" +
41 " nome = ?, " +
42 " sigla = ? " +
43 "WHERE" +
44 " id = ?;";
45
46 PreparedStatement stmt = getConnection().prepareStatement( sql );
47 stmt.setString( 1, obj.getNome() );
48 stmt.setString( 2, obj.getSigla() );
49 stmt.setInt( 3, obj.getId() );
50
51 stmt.executeUpdate();
52 stmt.close();
53
54 }
55
56 @Override
57 public void excluir( Pais obj ) throws SQLException {
58
59 String sql = "DELETE FROM pais WHERE id = ?;";
60
61 PreparedStatement stmt = getConnection().prepareStatement( sql );
62 stmt.setInt( 1, obj.getId() );
63
64 stmt.executeUpdate();
65 stmt.close();
66
67 }
68
69 @Override
70 public List<Pais> listarTodos() throws SQLException {
71
72 List<Pais> lista = new ArrayList<>();
73 String sql = "SELECT * FROM pais;";
74
75 PreparedStatement stmt = getConnection().prepareStatement( sql );
76 ResultSet rs = stmt.executeQuery();
90 CAPÍTULO 4. PADRÕES DE PROJETO: FACTORY, DAO E MVC
77
78 while ( rs.next() ) {
79
80 Pais pais = new Pais();
81 pais.setId( rs.getInt( "id" ) );
82 pais.setNome( rs.getString( "nome" ) );
83 pais.setSigla( rs.getString( "sigla" ) );
84
85 lista.add( pais );
86
87 }
88
89 rs.close();
90 stmt.close();
91
92 return lista;
93
94 }
95
96 @Override
97 public Pais obterPorId( int id ) throws SQLException {
98
99 Pais pais = null;
100 String sql = "SELECT * FROM pais WHERE id = ?;";
101
102 PreparedStatement stmt = getConnection().prepareStatement( sql );
103 stmt.setInt( 1, id );
104
105 ResultSet rs = stmt.executeQuery();
106
107 if ( rs.next() ) {
108
109 pais = new Pais();
110 pais.setId( rs.getInt( "id" ) );
111 pais.setNome( rs.getString( "nome" ) );
112 pais.setSigla( rs.getString( "sigla" ) );
113
114 }
115
116 rs.close();
117 stmt.close();
118
4.4. PADRÃO DE PROJETO DATA ACCESS OBJECT (DAO) 91
| Saiba Mais
Quer saber mais sobre ORM? Dê uma olhada nesses links: <http://pt.wikipedia
.org/wiki/Mapeamento_objeto-relacional>, <http://en.wikipedia.org/wiki/
Object-relational_mapping>, <http://www.hibernate.org/>
extensivamente no projeto que iremos criar. Tenho certeza que você vai achar muito
legal e útil! Como de costume, pratique o que você aprendeu durante este Capítulo
com as atividades de aprendizagem.
4.6 Resumo
Neste Capítulo demos um passo muito importante na nossa vida como desenvolve-
dores. Nós aprendemos que existem os chamados “padrões de projeto” ou “design
patterns”, que são padrões que guiam os desenvolvedores na solução de problemas
recorrentes através do uso de soluções de sucesso. Estudamos os padrões Factory e
DAO implementando exemplos e aprendemos o básico do funcionamento do padrão
MVC. No Capítulo 5 iremos implementar um projeto completo usando esses três
padrões.
4.7 Exercícios
Exercício 4.1: Defina, com suas palavras, o padrão Factory.
4.8 Projetos
Projeto 4.1: Da mesma forma que fizemos para a tabela pais, crie uma tabela na base
de dados testes_padroes, usando o MySQL Workbench, com o nome de “fruta”. Essa
tabela deve ter como colunas um campo identificador ( INT ), um campo que armaze-
nará o nome da fruta ( VARCHAR ) e um campo para armazenar a cor predominante da
fruta ( VARCHAR ). Implemente, no projeto que criamos durante este Capítulo, a enti-
dade Fruta e a classe FrutaDAO. Crie uma classe de testes chamada TesteFrutaDAO
para testar os métodos do DAO da fruta.
Projeto 4.2: Repita o Projeto 4.1, só que agora para a tabela “carro”. Um carro deve
ter um identificador, um nome ( VARCHAR ), um modelo ( VARCHAR ) e um ano de
fabricação ( INT ).
Projeto 4.3: Repita o Projeto 4.1, só que agora para a tabela “produto”. Um produto
deve ter um identificador, uma descrição ( VARCHAR ) e uma quantidade em estoque
( INT ).
CAPÍTULO
5
S ISTEMA PARA C ONTROLE DE C LIENTES
Aristóteles
N
ESTE Capítulo teremos como objetivos entender e realizar a construção
de uma aplicação Web em Java completa.
5.1 Introdução
Chegou a hora de colocar em prática tudo o que aprendemos nos Capítulos anteriores
com o objetivo de criar uma aplicação Web em Java completa. Iremos passar por todos
os passos do desenvolvimento da aplicação para que no Capítulo 6, você possa usar
um conjunto de requisitos para desenvolver um sistema sozinho. Vamos começar?
95
96 CAPÍTULO 5. SISTEMA PARA CONTROLE DE CLIENTES
também deseja que seja possível manter um cadastro de cidades e de estados, sendo
que as cidades devem ter um nome e um estado, enquanto um estado deve ter um
nome e uma sigla. Cada um dos cadastros (cliente, cidade e estado), deve conter as
funcionalidades de inserir, alterar e excluir um determinado registro.
Vamos analisar esses requisitos. Primeiramente, vamos identificar os tipos de entida-
des que farão parte do sistema, fazendo a seguinte pergunta: Quais são os tipos de
“coisas” que o sistema deve gerenciar? O sistema deve manter um cadastro de Clientes,
um cadastro de Cidades e um cadastro de Estados. Sendo assim, identificamos três
entidades, ou seja, Cliente, Cidade e Estado.
Cada um desses tipos de entidade tem uma determinada lista de características ou
atributos. Vamos organizá-las em uma tabela. Veja essa organização na Tabela 5.1.
Sabemos que esses tipos de entidade que foram identificados se tornarão tabelas na
nossa base de dados relacional não é mesmo? Cada atributo de cada tipo de entidade
se tornará uma coluna na tabela correspondente. Sabemos também que cada registro
de uma determinada tabela precisa ser diferenciado dos outros não é mesmo? Para
isso analisamos as tabelas até que consigamos identificar as chaves primárias de cada
uma delas. Uma chave primária é o conjunto mínimo de um ou mais atributos de
um determinado tipo de entidade que garante a unicidade de um registro, sendo
assim, precisamos encontrar, na lista de características de cada entidade, uma ou
mais características que, usadas em conjunto, garantem que um registro é diferente
5.2. ANALISANDO OS REQUISITOS 97
de outro. Tomemos como exemplo o tipo de entidade Estado. Veja na Tabela 5.2 uma
lista de registros da tabela estado do nosso provável banco de dados.
Tabela: estado
nome sigla
São Paulo SP
Rio de Janeiro RJ
Minas Gerais MG
... ...
Fonte: Elaborada pelo autor
para referenciar colunas de outras tabelas, não Strings. Imagine definir a chave primá-
ria de um Cliente como CPF. Ao precisarmos referenciar um cliente em outra tabela,
digamos uma tabela de pedidos, precisaríamos ter uma cópia do CPF do cliente em
cada pedido, gastando cerca de 11 a 14 caracteres (um CPF é no formato 000.000.000-
00), ao passo que poderíamos usar apenas um número! Veja, se cada caractere ocupar
4 bytes em disco/memória, um CPF de 11 dígitos (só os números) ocupará 44 bytes
(352 bits), enquanto um número inteiro provavelmente ocupará 4 ou 8 bytes (32 ou 64
bits). Pense nas implicações! Sabendo de tudo isso, podemos partir para o projeto do
banco de dados.
Usei o MySQL Workbench para criar um DER com essas tabelas. Veja como ficou na
Figura 5.1.
100 CAPÍTULO 5. SISTEMA PARA CONTROLE DE CLIENTES
Vamos agora implementar o banco de dados. No MySQL Workbench, crie uma nova
base de dados com o nome de cadastro_clientes como feito na Seção 4.2. Com a
base criada, torne-a padrão, abra uma aba para digitar código SQL e copie o código
da Listagem 5.1 no editor e execute o script.
Ao fazer esses três passos, temos nossas três tabelas criadas dentro da base de da-
dos. Expanda o banco cadastro_clientes e então expanda o nó Tables . Lá dentro
estarão as três tabelas criadas. Muito bem, terminamos a implementação do banco.
Vamos agora criar um diagrama de classes para representar cada uma das nossas
entidades, que serão mapeamentos das nossas tabelas no mundo orientado a objetos.
102 CAPÍTULO 5. SISTEMA PARA CONTROLE DE CLIENTES
Outro detalhe é que criei apenas o diagrama das classes que são as entidades do
sistema, não me preocupando com as outras classes que nosso sistema conterá. No-
vamente, como no exemplo do nosso banco de dados, em um sistema de verdade,
normalmente são desenvolvidos diagramas de classes muito mais completos e com-
plexos, dependendo do nível de representação que se deseja, bem como outros tipos
de diagramas UML que forem necessários. Com tudo isso pronto, podemos partir
para o desenvolvimento do sistema propriamente dito! Novamente, essa não é a nossa
preocupação neste livro.
Com a estrutura configurada, vamos agora copiar algumas classes do projeto “Padro-
esEmPatrica” que criamos no Capítulo 4. Para isso abra esse projeto, se ainda não
estiver aberto, no NetBeans. Expanda o pacote “padroesempratica.jdbc”, clique
com o botão direito no arquivo “ConnectionFactory.java” e escolha Copy . Volte
ao projeto “CadastroClientes”, clique com o botão direito no pacote “cadastroclien-
tes.jdbc”, escolha Paste e então Refactor Copy... . Um diálogo será aberto. Clique
no botão Refactor . O arquivo será copiado para o projeto e as alterações que fo-
rem necessárias fazer no arquivo, como mudar a cláusula package , serão feitas
pelo NetBeans. Faça o mesmo processo para o arquivo “DAO.java” contido no pacote
“pradoesempratica.dao” do projeto “PadroesEmPratica”, copiando-o no pacote “ca-
dastroclientes.dao” do projeto “CadastroClientes”. Talvez o NetBeans aponte um erro
104 CAPÍTULO 5. SISTEMA PARA CONTROLE DE CLIENTES
no arquivo depois da cópia. Para corrigir, abra o arquivo no editor e altere o import
que está com problema.
Outro detalhe é que precisamos mudar a URL da nossa fábrica de conexões para fazer
com que as conexões criadas sejam relativas à base de dados cadastro_clientes.
Para isso, abra o arquivo “ConnectionFactory.java” do pacote
“cadastroclientes.jdbc” e mude a URL de:
jdbc:mariadb://localhost/testes_padroes
Para:
jdbc:mariadb://localhost/cadastro_clientes
Agora vamos preparar toda a camada de persistência. No pacote
“cadastroclientes.entidades”, crie três classes: “Estado”, “Cidade” e “Cliente”
(sem as aspas). Nas três listagens a seguir estão listados os códigos-fonte das três
classes. Note que estou omitindo os gets e os sets, mas isso não significa que eles não
devam existir. Fica por sua conta criá-los ok? Não se esqueça de fazer isso!
1 package cadastroclientes.entidades;
2
3 /**
4 * Entidade Estado.
5 *
6 * @author Prof. Dr. David Buzatto
7 */
8 public class Estado {
9
10 private int id;
11 private String nome;
12 private String sigla;
13
14 public int getId() {
15 return id;
16 }
17
18 public void setId( int id ) {
19 this.id = id;
20 }
21
5.5. CONSTRUINDO O SISTEMA 105
1 package cadastroclientes.entidades;
2
3 /**
4 * Entidade Cidade.
5 *
6 * @author Prof. Dr. David Buzatto
7 */
8 public class Cidade {
9
10 private int id;
11 private String nome;
12 private Estado estado;
13
14 public int getId() {
15 return id;
16 }
17
18 public void setId( int id ) {
19 this.id = id;
20 }
106 CAPÍTULO 5. SISTEMA PARA CONTROLE DE CLIENTES
21
22 public String getNome() {
23 return nome;
24 }
25
26 public void setNome( String nome ) {
27 this.nome = nome;
28 }
29
30 public Estado getEstado() {
31 return estado;
32 }
33
34 public void setEstado( Estado estado ) {
35 this.estado = estado;
36 }
37
38 }
1 package cadastroclientes.entidades;
2
3 import java.sql.Date;
4
5 /**
6 * Entidade Cliente.
7 *
8 * @author Prof. Dr. David Buzatto
9 */
10 public class Cliente {
11
12 private int id;
13 private String nome;
14 private String sobrenome;
15 private Date dataNascimento;
16 private String cpf;
17 private String email;
18 private String logradouro;
19 private String numero;
5.5. CONSTRUINDO O SISTEMA 107
62 }
63
64 public String getEmail() {
65 return email;
66 }
67
68 public void setEmail( String email ) {
69 this.email = email;
70 }
71
72 public String getLogradouro() {
73 return logradouro;
74 }
75
76 public void setLogradouro( String logradouro ) {
77 this.logradouro = logradouro;
78 }
79
80 public String getNumero() {
81 return numero;
82 }
83
84 public void setNumero( String numero ) {
85 this.numero = numero;
86 }
87
88 public String getBairro() {
89 return bairro;
90 }
91
92 public void setBairro( String bairro ) {
93 this.bairro = bairro;
94 }
95
96 public String getCep() {
97 return cep;
98 }
99
100 public void setCep( String cep ) {
101 this.cep = cep;
102 }
103
5.5. CONSTRUINDO O SISTEMA 109
1 package cadastroclientes.dao;
2
3 import cadastroclientes.entidades.Estado;
4 import java.sql.PreparedStatement;
5 import java.sql.ResultSet;
6 import java.sql.SQLException;
7 import java.util.ArrayList;
8 import java.util.List;
9
10 /**
11 * DAO para a entidade Estado.
12 *
13 * @author Prof. Dr. David Buzatto
14 */
15 public class EstadoDAO extends DAO<Estado> {
16
17 public EstadoDAO() throws SQLException {
18 }
19
20 @Override
21 public void salvar( Estado obj ) throws SQLException {
22
23 PreparedStatement stmt = getConnection().prepareStatement(
24 "INSERT INTO " +
110 CAPÍTULO 5. SISTEMA PARA CONTROLE DE CLIENTES
67 stmt.close();
68
69 }
70
71 @Override
72 public List<Estado> listarTodos() throws SQLException {
73
74 List<Estado> lista = new ArrayList<>();
75
76 PreparedStatement stmt = getConnection().prepareStatement(
77 "SELECT * FROM estado " +
78 "ORDER BY nome, sigla;" );
79
80 ResultSet rs = stmt.executeQuery();
81
82 while ( rs.next() ) {
83
84 Estado e = new Estado();
85
86 e.setId( rs.getInt( "id" ) );
87 e.setNome( rs.getString( "nome" ) );
88 e.setSigla( rs.getString( "sigla" ) );
89
90 lista.add( e );
91
92 }
93
94 rs.close();
95 stmt.close();
96
97 return lista;
98
99 }
100
101 @Override
102 public Estado obterPorId( int id ) throws SQLException {
103
104 Estado estado = null;
105
106 PreparedStatement stmt = getConnection().prepareStatement(
107 "SELECT * FROM estado " +
108 "WHERE id = ?;" );
112 CAPÍTULO 5. SISTEMA PARA CONTROLE DE CLIENTES
109
110 stmt.setInt( 1, id );
111
112 ResultSet rs = stmt.executeQuery();
113
114 if ( rs.next() ) {
115
116 estado = new Estado();
117
118 estado.setId( rs.getInt( "id" ) );
119 estado.setNome( rs.getString( "nome" ) );
120 estado.setSigla( rs.getString( "sigla" ) );
121
122 }
123
124 rs.close();
125 stmt.close();
126
127 return estado;
128
129 }
130
131 }
1 package cadastroclientes.dao;
2
3 import cadastroclientes.entidades.Cidade;
4 import cadastroclientes.entidades.Estado;
5 import java.sql.PreparedStatement;
6 import java.sql.ResultSet;
7 import java.sql.SQLException;
8 import java.util.ArrayList;
9 import java.util.List;
10
11 /**
12 * DAO para a entidade Cidade.
13 *
14 * @author Prof. Dr. David Buzatto
5.5. CONSTRUINDO O SISTEMA 113
15 */
16 public class CidadeDAO extends DAO<Cidade> {
17
18 public CidadeDAO() throws SQLException {
19 }
20
21 @Override
22 public void salvar( Cidade obj ) throws SQLException {
23
24 PreparedStatement stmt = getConnection().prepareStatement(
25 "INSERT INTO " +
26 "cidade( nome, estado_id ) " +
27 "VALUES( ?, ? );" );
28
29 stmt.setString( 1, obj.getNome() );
30 stmt.setInt( 2, obj.getEstado().getId() );
31
32 stmt.executeUpdate();
33 stmt.close();
34
35 }
36
37 @Override
38 public void atualizar( Cidade obj ) throws SQLException {
39
40 PreparedStatement stmt = getConnection().prepareStatement(
41 "UPDATE cidade " +
42 "SET" +
43 " nome = ?," +
44 " estado_id = ? " +
45 "WHERE" +
46 " id = ?;" );
47
48 stmt.setString( 1, obj.getNome() );
49 stmt.setInt( 2, obj.getEstado().getId() );
50 stmt.setInt( 3, obj.getId() );
51
52 stmt.executeUpdate();
53 stmt.close();
54
55 }
56
114 CAPÍTULO 5. SISTEMA PARA CONTROLE DE CLIENTES
57 @Override
58 public void excluir( Cidade obj ) throws SQLException {
59
60 PreparedStatement stmt = getConnection().prepareStatement(
61 "DELETE FROM cidade " +
62 "WHERE" +
63 " id = ?;" );
64
65 stmt.setInt( 1, obj.getId() );
66
67 stmt.executeUpdate();
68 stmt.close();
69
70 }
71
72 @Override
73 public List<Cidade> listarTodos() throws SQLException {
74
75 List<Cidade> lista = new ArrayList<>();
76
77 PreparedStatement stmt = getConnection().prepareStatement(
78 "SELECT" +
79 " c.id idCidade, " +
80 " c.nome nomeCidade, " +
81 " e.id idEstado, " +
82 " e.nome nomeEstado, " +
83 " e.sigla siglaEstado " +
84 "FROM" +
85 " cidade c, " +
86 " estado e " +
87 "WHERE" +
88 " c.estado_id = e.id " +
89 "ORDER BY c.nome, e.nome, e.sigla;" );
90
91 ResultSet rs = stmt.executeQuery();
92
93 while ( rs.next() ) {
94
95 Cidade c = new Cidade();
96 Estado e = new Estado();
97
98 c.setId( rs.getInt( "idCidade" ) );
5.5. CONSTRUINDO O SISTEMA 115
141
142 cidade = new Cidade();
143 Estado e = new Estado();
144
145 cidade.setId( rs.getInt( "idCidade" ) );
146 cidade.setNome( rs.getString( "nomeCidade" ) );
147 cidade.setEstado( e );
148
149 e.setId( rs.getInt( "idEstado" ) );
150 e.setNome( rs.getString( "nomeEstado" ) );
151 e.setSigla( rs.getString( "siglaEstado" ) );
152
153 }
154
155 rs.close();
156 stmt.close();
157
158 return cidade;
159
160 }
161
162
163 }
1 package cadastroclientes.dao;
2
3 import cadastroclientes.entidades.Cidade;
4 import cadastroclientes.entidades.Cliente;
5 import cadastroclientes.entidades.Estado;
6 import java.sql.PreparedStatement;
7 import java.sql.ResultSet;
8 import java.sql.SQLException;
9 import java.util.ArrayList;
10 import java.util.List;
11
12 /**
13 * DAO para a entidade Cliente.
14 *
5.5. CONSTRUINDO O SISTEMA 117
99
100 stmt.setInt( 1, obj.getId() );
101
102 stmt.executeUpdate();
103 stmt.close();
104
105 }
106
107 @Override
108 public List<Cliente> listarTodos() throws SQLException {
109
110 List<Cliente> lista = new ArrayList<>();
111
112 PreparedStatement stmt = getConnection().prepareStatement(
113 "SELECT" +
114 " c.id idCliente, " +
115 " c.nome nomeCliente, " +
116 " c.sobreNome sobrenomeCliente, " +
117 " c.dataNascimento dataNascimentoCliente, " +
118 " c.cpf cpfCliente, " +
119 " c.email emailCliente, " +
120 " c.logradouro logradouroCliente, " +
121 " c.numero numeroCliente, " +
122 " c.bairro bairroCliente, " +
123 " c.cep cepCliente, " +
124 " ci.id idCidade, " +
125 " ci.nome nomeCidade, " +
126 " e.id idEstado, " +
127 " e.nome nomeEstado, " +
128 " e.sigla siglaEstado " +
129 "FROM" +
130 " cliente c, " +
131 " cidade ci, " +
132 " estado e " +
133 "WHERE" +
134 " c.cidade_id = ci.id AND " +
135 " ci.estado_id = e.id " +
136 "ORDER BY c.nome, c.sobreNome, ci.nome;" );
137
138 ResultSet rs = stmt.executeQuery();
139
140 while ( rs.next() ) {
120 CAPÍTULO 5. SISTEMA PARA CONTROLE DE CLIENTES
141
142 Cliente c = new Cliente();
143 Cidade ci = new Cidade();
144 Estado e = new Estado();
145
146 c.setId( rs.getInt( "idCliente" ) );
147 c.setNome( rs.getString( "nomeCliente" ) );
148 c.setSobrenome( rs.getString( "sobrenomeCliente" ) );
149 c.setDataNascimento( rs.getDate( "dataNascimentoCliente" ) );
150 c.setCpf( rs.getString( "cpfCliente" ) );
151 c.setEmail( rs.getString( "emailCliente" ) );
152 c.setLogradouro( rs.getString( "logradouroCliente" ) );
153 c.setNumero( rs.getString( "numeroCliente" ) );
154 c.setBairro( rs.getString( "bairroCliente" ) );
155 c.setCep( rs.getString( "cepCliente" ) );
156 c.setCidade( ci );
157
158 ci.setId( rs.getInt( "idCidade" ) );
159 ci.setNome( rs.getString( "nomeCidade" ) );
160 ci.setEstado( e );
161
162 e.setId( rs.getInt( "idEstado" ) );
163 e.setNome( rs.getString( "nomeEstado" ) );
164 e.setSigla( rs.getString( "siglaEstado" ) );
165
166 lista.add( c );
167
168 }
169
170 rs.close();
171 stmt.close();
172
173 return lista;
174
175 }
176
177 @Override
178 public Cliente obterPorId( int id ) throws SQLException {
179
180 Cliente cliente = null;
181
182 PreparedStatement stmt = getConnection().prepareStatement(
5.5. CONSTRUINDO O SISTEMA 121
183 "SELECT" +
184 " c.id idCliente, " +
185 " c.nome nomeCliente, " +
186 " c.sobreNome sobrenomeCliente, " +
187 " c.dataNascimento dataNascimentoCliente, " +
188 " c.cpf cpfCliente, " +
189 " c.email emailCliente, " +
190 " c.logradouro logradouroCliente, " +
191 " c.numero numeroCliente, " +
192 " c.bairro bairroCliente, " +
193 " c.cep cepCliente, " +
194 " ci.id idCidade, " +
195 " ci.nome nomeCidade, " +
196 " e.id idEstado, " +
197 " e.nome nomeEstado, " +
198 " e.sigla siglaEstado " +
199 "FROM" +
200 " cliente c, " +
201 " cidade ci, " +
202 " estado e " +
203 "WHERE" +
204 " c.id = ? AND " +
205 " c.cidade_id = ci.id AND " +
206 " ci.estado_id = e.id;" );
207
208 stmt.setInt( 1, id );
209
210 ResultSet rs = stmt.executeQuery();
211
212 if ( rs.next() ) {
213
214 cliente = new Cliente();
215 Cidade ci = new Cidade();
216 Estado e = new Estado();
217
218 cliente.setId( rs.getInt( "idCliente" ) );
219 cliente.setNome( rs.getString( "nomeCliente" ) );
220 cliente.setSobrenome( rs.getString( "sobrenomeCliente" ) );
221 cliente.setDataNascimento( rs.getDate( "dataNascimentoCliente" )
,→ );
222 cliente.setCpf( rs.getString( "cpfCliente" ) );
223 cliente.setEmail( rs.getString( "emailCliente" ) );
122 CAPÍTULO 5. SISTEMA PARA CONTROLE DE CLIENTES
Quantos códigos hein? Copiou tudo? Crie algumas classes de teste no pacote
“cadastroclientes.teste” e teste a persistência de cada entidade. Com isso, termi-
namos a parte da persistência do nosso projeto.
Agora nós vamos começar a implementar as visualizações e os controladores. Nossa
aplicação terá três links no index.jsp, sendo que cada link levará a um determinado
cadastro. Cada cadastro vai conter uma página principal onde todos os itens desse
cadastro serão exibidos e onde poderão ser alterados, excluídos ou então poderemos
cadastrar um novo item.
Vamos começar pelo cadastro de estados. Na pasta “estados” dentro da pasta
“formularios”, crie um arquivo JSP com o nome de “listagem.jsp” (sem as as-
pas). Nesse arquivo vão ser listados todos os estados, portanto precisamos obter esses
estados de alguma forma. Você se lembra que nos nossos DAOs existe um método cha-
mado listarTodos() que retorna todos os registros de uma determinada tabela?
Nós não vamos usar esse método diretamente no JSP, então vamos criar uma classe de
serviços que vai instanciar o DAO, gerar a lista e fechar a conexão para nós. Nos paco-
5.5. CONSTRUINDO O SISTEMA 123
1 package cadastroclientes.servicos;
2
3 import cadastroclientes.dao.EstadoDAO;
4 import cadastroclientes.entidades.Estado;
5 import java.sql.SQLException;
6 import java.util.ArrayList;
7 import java.util.List;
8
9 /**
10 * Classe de serviços para a entidade Estado.
11 *
12 * @author Prof. Dr. David Buzatto
13 */
14 public class EstadoServices {
15
16 /**
17 * Usa o EstadoDAO para obter todos os estados.
18 *
19 * @return Lista de estados.
20 */
21 public List<Estado> getTodos() {
22
23 List<Estado> lista = new ArrayList<>();
24 EstadoDAO dao = null;
25
26 try {
27 dao = new EstadoDAO();
28 lista = dao.listarTodos();
29 } catch ( SQLException exc ) {
30 exc.printStackTrace();
31 } finally {
32 if ( dao != null ) {
33 try {
34 dao.fecharConexao();
124 CAPÍTULO 5. SISTEMA PARA CONTROLE DE CLIENTES
Criamos essa classe para que ela encapsule todo o processo de obtenção da lista de
estados. Note que é no método getTodos() que o EstadoDAO vai ser instanciado e
gerenciado.
Agora que temos o JSP que vai obter a lista de estados para nós, além de gerenciar
o DAO, nós podemos implementar o nosso arquivo de listagem de estados. Abra o
arquivo /formularios/estados/listagem.jsp e copie o código da Listagem 5.10.
19 <h1>Estados Cadastrados</h1>
20
21 <p>
22 <a href="${cp}/formularios/estados/novo.jsp">
23 Novo Estado
24 </a>
25 </p>
26
27 <table class="tabelaListagem">
28 <thead>
29 <tr>
30 <th>Id</th>
31 <th>Nome</th>
32 <th>Sigla</th>
33 <th>Alterar</th>
34 <th>Excluir</th>
35 </tr>
36 </thead>
37 <tbody>
38
39 <jsp:useBean
40 id="servicos"
41 scope="page"
42 class="cadastroclientes.servicos.EstadoServices"/>
43
44 <c:forEach items="${servicos.todos}" var="estado">
45 <tr>
46 <td>${estado.id}</td>
47 <td>${estado.nome}</td>
48 <td>${estado.sigla}</td>
49 <td>
50 <a href="${cp}/${prefixo}Alteracao&id=${estado.id}">
51 Alterar
52 </a>
53 </td>
54 <td>
55 <a href="${cp}/${prefixo}Exclusao&id=${estado.id}">
56 Excluir
57 </a>
58 </td>
59 </tr>
60 </c:forEach>
126 CAPÍTULO 5. SISTEMA PARA CONTROLE DE CLIENTES
61 </tbody>
62
63 </table>
64
65 <p>
66 <a href="${cp}/formularios/estados/novo.jsp">
67 Novo Estado
68 </a>
69 </p>
70
71 <p><a href="${cp}/index.jsp">Tela Principal</a></p>
72
73 </body>
74
75 </html>
Perceba que a identação do código foi feita com dois espaços para economizar espaço
nas listagens, mas você pode manter os quatro espaços usados por padrão.
Vamos analisar o código, detalhando as novidades que aparecerem. Nas linhas 3
e 4 usamos a tag <c:set> para configurar no escopo da página dois valores que
usaremos no código. Um deles, chamado de cp será o caminho do contexto da apli-
cação, obtido através da instrução ${pageContext.request.contextPath} . Essa
instrução da EL retornará no nosso caso o valor /CadastroClientes, pois é esse
o contexto da aplicação configurado na criação do projeto. Faremos dessa forma
para que, independente do contexto, ele seja obtido apropriadamente. Estamos fa-
zendo isso para que possamos configurar sempre caminhos absolutos para os nossos
recursos, evitando problemas de referenciamento relativo. Poderemos acessar esse
valor agora usando a construção ${cp} . O mesmo acontece na linha 4, onde o valor
processaEstados?acao=preparar é configurado na variável prefixo. Veremos o
motivo adiante.
Entre as linhas 21 e 25, criamos um link que aponta para o arquivo
/CadastroClientes/formularios/estado/novo.jsp –que ainda não implemen-
tamos– e que será o formulário responsável em criar um novo estado. Perceba que
usamos a variável de página cp para obter o contexto da aplicação. Este mesmo código
é repetido entre as linhas 65 e 69. Na linha 27, abrimos a tag de uma tabela e, até a
linha 36, criamos seu cabeçalho. Na linha 31, usamos a tag <jsp:useBean> para
instanciar um objeto do tipo EstadoServices, que contém o método que vamos usar
para obter a lista de estados. Demos o nome de servicos para essa instância. Na
linha 44, usamos um <c:forEach> para iterar sobre a lista retornada pelo método
5.5. CONSTRUINDO O SISTEMA 127
1 body {
2 font-family: Verdana,Arial,Helvetica,sans-serif;
3 font-size: 14px;
4 background-color: #FFFFFF;
128 CAPÍTULO 5. SISTEMA PARA CONTROLE DE CLIENTES
5 }
6
7 a {
8 color: #0484AE;
9 text-decoration: none;
10 font-weight: bold;
11 }
12
13 a:hover {
14 color: #000000;
15 text-decoration: underline;
16 }
17
18 .tabelaListagem {
19 background: #000000;
20 }
21
22 .tabelaListagem th {
23 background: #EEEEEE;
24 padding: 5px;
25 }
26
27 .tabelaListagem td {
28 background: #FFFFFF;
29 padding: 5px;
30 }
31
32 .alinharDireita {
33 text-align: right;
34 }
Execute o projeto e aponte o navegador para a página de listagem para ver como está
ficando. Se você já tiver alguns estados previamente cadastrados via SQL, vai ver que
eles aparecerão na tabela. Vamos agora criar nosso formulário para criar estados. Na
pasta /formularios/estados, crie um arquivo JSP chamado “novo” (sem as aspas).
O código-fonte do arquivo pode ser visto na Listagem 5.13.
5.5. CONSTRUINDO O SISTEMA 129
40 size="3"
41 maxlength="2"
42 required/>
43 </td>
44 </tr>
45 <tr>
46 <td>
47 <a href="${cp}/formularios/estados/listagem.jsp">
48 Voltar
49 </a>
50 </td>
51 <td class="alinharDireita">
52 <input type="submit" value="Salvar"/>
53 </td>
54 </tr>
55 </table>
56
57 </form>
58
59 </body>
60
61 </html>
Salve o arquivo e veja se ele está sendo exibido corretamente no navegador. Acesse-o
pelo link “Novo Estado” da página de listagem de estados. Vamos analisar o código.
Na linha 12 referenciamos o nosso arquivo de estilos. Na linha 20 declaramos a tag
do formulário. Note que a action está apontando para ${cp}/processaEstados
que será a URL que mapearemos o Servlet que tratará os estados. A novidade nesse
formulário é o uso de um input do tipo hidden (escondido). Esses tipos de input são
usados para guardar valores que o usuário do sistema não tem acesso diretamente. No
nosso caso, esse input, que tem o nome de acao e valor inserir, vai indicar ao Servlet
que queremos criar um novo estado. Nós precisamos tratar isso na implementação
do nosso Servlet ok? Além disso, perceba que utilizamos alguns atributos nos inputs
para que nosso formulário seja validado antes de ser submetido. Por exemplo, o input
do atributo nome do estado tem tamanho máximo de 30 caracteres (maxlength) e é
obrigatório (required). Como já temos nossa página de listagem e o nosso formulário
de criação de estados, está na hora de criarmos o Servlet que vai gerenciar isso. No
pacote cadastroclientes.controladores, crie um Servlet com o nome de “Esta-
dosServlet” (sem as aspas) e configure o mapeamento dele para “/processaEstados”
(sem as aspas).
A seguir, na Listagem 5.14 é apresentado o código completo da classe EstadosServlet.
5.5. CONSTRUINDO O SISTEMA 131
1 package cadastroclientes.controladores;
2
3 import cadastroclientes.dao.EstadoDAO;
4 import cadastroclientes.entidades.Estado;
5 import java.io.IOException;
6 import java.sql.SQLException;
7 import jakarta.servlet.RequestDispatcher;
8 import jakarta.servlet.ServletException;
9 import jakarta.servlet.annotation.WebServlet;
10 import jakarta.servlet.http.HttpServlet;
11 import jakarta.servlet.http.HttpServletRequest;
12 import jakarta.servlet.http.HttpServletResponse;
13
14 /**
15 * Servlet para tratar Estados.
16 *
17 * @author Prof. Dr. David Buzatto
18 */
19 @WebServlet( name = "EstadosServlet",
20 urlPatterns = { "/processaEstados" } )
21 public class EstadosServlet extends HttpServlet {
22
23 protected void processRequest(
24 HttpServletRequest request,
25 HttpServletResponse response )
26 throws ServletException, IOException {
27
28 String acao = request.getParameter( "acao" );
29 EstadoDAO dao = null;
30 RequestDispatcher disp = null;
31
32 try {
33
34 dao = new EstadoDAO();
35
36 if ( acao.equals( "inserir" ) ) {
37
38 String nome = request.getParameter( "nome" );
39 String sigla = request.getParameter( "sigla" );
132 CAPÍTULO 5. SISTEMA PARA CONTROLE DE CLIENTES
40
41 Estado e = new Estado();
42 e.setNome( nome );
43 e.setSigla( sigla );
44
45 dao.salvar( e );
46
47 disp = request.getRequestDispatcher(
48 "/formularios/estados/listagem.jsp" );
49
50 } else if ( acao.equals( "alterar" ) ) {
51
52 int id = Integer.parseInt(request.getParameter( "id" ));
53 String nome = request.getParameter( "nome" );
54 String sigla = request.getParameter( "sigla" );
55
56 Estado e = new Estado();
57 e.setId( id );
58 e.setNome( nome );
59 e.setSigla( sigla );
60
61 dao.atualizar( e );
62
63 disp = request.getRequestDispatcher(
64 "/formularios/estados/listagem.jsp" );
65
66 } else if ( acao.equals( "excluir" ) ) {
67
68 int id = Integer.parseInt(request.getParameter( "id" ));
69
70 Estado e = new Estado();
71 e.setId( id );
72
73 dao.excluir( e );
74
75 disp = request.getRequestDispatcher(
76 "/formularios/estados/listagem.jsp" );
77
78 } else {
79
80 int id = Integer.parseInt(request.getParameter( "id" ));
81 Estado e = dao.obterPorId( id );
5.5. CONSTRUINDO O SISTEMA 133
82 request.setAttribute( "estado", e );
83
84 if ( acao.equals( "prepararAlteracao" ) ) {
85 disp = request.getRequestDispatcher(
86 "/formularios/estados/alterar.jsp" );
87 } else if ( acao.equals( "prepararExclusao" ) ) {
88 disp = request.getRequestDispatcher(
89 "/formularios/estados/excluir.jsp" );
90 }
91
92 }
93
94 } catch ( SQLException exc ) {
95 exc.printStackTrace();
96 } finally {
97 if ( dao != null ) {
98 try {
99 dao.fecharConexao();
100 } catch ( SQLException exc ) {
101 exc.printStackTrace();
102 }
103 }
104 }
105
106 if ( disp != null ) {
107 disp.forward( request, response );
108 }
109
110 }
111
112 @Override
113 protected void doGet(
114 HttpServletRequest request,
115 HttpServletResponse response )
116 throws ServletException, IOException {
117 processRequest( request, response );
118 }
119
120 @Override
121 protected void doPost(
122 HttpServletRequest request,
123 HttpServletResponse response )
134 CAPÍTULO 5. SISTEMA PARA CONTROLE DE CLIENTES
Perceba que a linha que contém o código para a configuração do encoding do request
que estávamos usando até agora foi removida pois, para não precisarmos ter essa con-
figuração em cada Servlet, criaremos um Filtro para realizar essa ação padrão para nós.
Os filtros das aplicações Web em Java são componentes que podem atuar antes e/ou
depois de requisições à outros componentes da aplicação, inclusive outros filtros. Para
isso, crie primeiramente o pacote “filtros” dentro do pacote
“controladores”. No pacote “filtros’, crie uma nova classe chamada
“ConfigurarEncodingFilter.java” e copie o código apresentado na Listagem 5.15.
Note que poderíamos ter criado o filtro usando a opção apropriada nos tipos de ar-
quivo disponíveis no NetBeans, entretanto, nosso filtro é bastante simples e todo o
código inserido pelo NetBeans quando criamos usando o template é praticamente
desnecessário para a nossa necessidade, sendo assim, faremos manualmente.
1 package cadastroclientes.controladores.filtros;
2
3 import java.io.IOException;
4 import jakarta.servlet.Filter;
5 import jakarta.servlet.FilterChain;
6 import jakarta.servlet.ServletException;
7 import jakarta.servlet.ServletRequest;
8 import jakarta.servlet.ServletResponse;
9 import jakarta.servlet.annotation.WebFilter;
10
11 /**
12 * Filtro para configurar o encoding das requisições de todos
5.5. CONSTRUINDO O SISTEMA 135
Veja que na linha 17 é usada anotação @WebFilter que será responsável em indi-
car ao servidor de aplicações que essa classe é um Filtro. O nome do filtro, indicado
pelo atributo filterName, é usado para indentificar o filtro, enquanto o atributo
urlPatterns indica em quais URLs esse filtro será aplicado. No nosso caso, quere-
mos que todas as requisições da aplicação passem por ele, então usamos o padrão
/*, onde o asterisco denota “tudo”. O método doFilter() realiza a atividade de
filtragem, sendo que será executado antes das requisições. Queremos sempre definir
a codificação para UTF-8 para padronizarmos o encoding que a aplicação utiliza,
evitando problemas com caracteres acentuados e símbolos especiais. Isso é feito na
linha 28. Na linha 29, dá-se a chance de outros filtros que porventura tenham sido
criados atuarem na requisição.
Voltando ao EstadosServlet apresentado na Listagem 5.14, copie todo o código e
execute o projeto. Acesse a listagem de estados e clique para criar um novo estado.
Preencha o formulário e salve. O novo estado será salvo e a listagem aparecerá nova-
mente. Teste a inserção de estados com nomes que contém palavras acentuadas, por
exemplo, “São Paulo” e verifique se os caracteres acentuados estão sendo persistidos
apropriadamente. Como exercício, tente entender o que está acontecendo no Servlet.
Todo o código apresentado já foi estudado nos exemplos anteriores. Perceba que
136 CAPÍTULO 5. SISTEMA PARA CONTROLE DE CLIENTES
foram tratadas todas as ações possíveis, mas ainda faltam dois arquivos JSP para im-
plementarmos. O alterar.jsp e o excluir.jsp. Vamos fazer isso? Crie um arquivo
JSP na pasta /formularios/estados com o nome de “alterar” (sem as aspas) e
copie o código da Listagem 5.16.
35 </tr>
36 <tr>
37 <td class="alinharDireita">Sigla:</td>
38 <td>
39 <input name="sigla"
40 type="text"
41 size="3"
42 maxlength="2"
43 value="${requestScope.estado.sigla}"/>
44 </td>
45 </tr>
46 <tr>
47 <td>
48 <a href="${cp}/formularios/estados/listagem.jsp">
49 Voltar
50 </a>
51 </td>
52 <td class="alinharDireita">
53 <input type="submit" value="Alterar"/>
54 </td>
55 </tr>
56 </table>
57
58 </form>
59
60 </body>
61
62 </html>
40 <td class="alinharDireita">
41 <input type="submit" value="Excluir"/>
42 </td>
43 </tr>
44 </table>
45
46 </form>
47
48 </body>
49
50 </html>
Copiou tudo? Teste! Verifique se tudo está funcionando corretamente. Antes de par-
tirmos para os outros cadastros, vamos alterar o index.jsp criando três links, um
para cada listagem dos cadastros. O novo código do index.jsp pode ser visto na
Listagem 5.18.
22 </p>
23 <p>
24 <a href="${cp}/formularios/cidades/listagem.jsp">Cidades</a>
25 </p>
26 <p>
27 <a href="${cp}/formularios/estados/listagem.jsp">Estados</a>
28 </p>
29
30 </body>
31
32 </html>
Teste o index.jsp e veja que agora ele tem três links para cada um dos cadastros. O
que falta agora para terminarmos nosso projeto é implementar todos os JSPs dos
outros cadastros, bem como seus respectivos Servlets e classes de serviço. A seguir,
vou listar cada um desses arquivos, agrupando-os por tipo de cadastro, sendo que
só irei comentar as linhas que contenham código que ainda não utilizamos ou que
precisem de alguma explicação. Não se esqueça de testar o projeto sempre que tiver
implementado os arquivos necessários para o funcionamento de uma determinada
funcionalidade. No cabeçalho de cada listagem é indicado o arquivo em que ela deve
estar, sendo assim lembre-se de criá-lo antes! Vamos começar?
1 package cadastroclientes.servicos;
2
3 import cadastroclientes.dao.CidadeDAO;
4 import cadastroclientes.entidades.Cidade;
5 import java.sql.SQLException;
6 import java.util.ArrayList;
7 import java.util.List;
8
9 /**
10 * Classe de serviços para a entidade Cidade.
11 *
12 * @author Prof. Dr. David Buzatto
13 */
14 public class CidadeServices {
15
16 /**
5.5. CONSTRUINDO O SISTEMA 141
9 <title>Cidades Cadastradas</title>
10 <meta charset="UTF-8">
11 <meta name="viewport"
12 content="width=device-width, initial-scale=1.0">
13 <link rel="stylesheet"
14 href="${cp}/css/estilos.css"/>
15 </head>
16
17 <body>
18
19 <h1>Cidades Cadastradas</h1>
20
21 <p>
22 <a href="${cp}/formularios/cidades/novo.jsp">
23 Nova Cidade
24 </a>
25 </p>
26
27 <table class="tabelaListagem">
28 <thead>
29 <tr>
30 <th>Id</th>
31 <th>Nome</th>
32 <th>Estado</th>
33 <th>Alterar</th>
34 <th>Excluir</th>
35 </tr>
36 </thead>
37 <tbody>
38
39 <jsp:useBean
40 id="servicos"
41 scope="page"
42 class="cadastroclientes.servicos.CidadeServices"/>
43
44 <c:forEach items="${servicos.todos}" var="cidade">
45 <tr>
46 <td>${cidade.id}</td>
47 <td>${cidade.nome}</td>
48 <td>${cidade.estado.sigla}</td>
49 <td>
50 <a href="${cp}/${prefixo}Alteracao&id=${cidade.id}">
5.5. CONSTRUINDO O SISTEMA 143
51 Alterar
52 </a>
53 </td>
54 <td>
55 <a href="${cp}/${prefixo}Exclusao&id=${cidade.id}">
56 Excluir
57 </a>
58 </td>
59 </tr>
60 </c:forEach>
61 </tbody>
62 </table>
63
64 <p>
65 <a href="${cp}/formularios/cidades/novo.jsp">
66 Nova Cidade
67 </a>
68 </p>
69
70 <p><a href="${cp}/index.jsp">Tela Principal</a></p>
71
72 </body>
73
74 </html>
14 </head>
15
16 <body>
17
18 <h1>Nova Cidade</h1>
19
20 <form method="post" action="${cp}/processaCidades">
21
22 <input name="acao" type="hidden" value="inserir"/>
23
24 <table>
25 <tr>
26 <td class="alinharDireita">Nome:</td>
27 <td>
28 <input name="nome"
29 type="text"
30 size="20"
31 maxlength="30"
32 required/>
33 </td>
34 </tr>
35 <tr>
36 <td class="alinharDireita">Estado:</td>
37 <td>
38
39 <jsp:useBean
40 id="servicos"
41 scope="page"
42 class="cadastroclientes.servicos.EstadoServices"/>
43
44 <select name="idEstado" required>
45 <c:forEach items="${servicos.todos}" var="estado">
46 <option value="${estado.id}">
47 ${estado.nome} - ${estado.sigla}
48 </option>
49 </c:forEach>
50 </select>
51
52 </td>
53 </tr>
54 <tr>
55 <td>
5.5. CONSTRUINDO O SISTEMA 145
56 <a href="${cp}/formularios/cidades/listagem.jsp">Voltar</a>
57 </td>
58 <td class="alinharDireita">
59 <input type="submit" value="Salvar"/>
60 </td>
61 </tr>
62 </table>
63
64 </form>
65
66 </body>
67
68 </html>
Note que na linha 45 da Listagem 5.21 nós usamos o serviço getTodos() da classe
EstadoServices para obter todos os estados cadastrados e com isso gerar as opções
(tag <option> ) do select (combo box). Note que o valor de cada option é o id asso-
ciado a determinado estado, enquanto o que aparece ao usuário é a concatenação do
nome e da sigla do mesmo estado. O id do estado selecionado será enviado ao Servlet
por meio do parâmetro idEstado, configurada no atributo name da tag <select> .
1 package cadastroclientes.controladores;
2
3 import cadastroclientes.dao.CidadeDAO;
4 import cadastroclientes.entidades.Cidade;
5 import cadastroclientes.entidades.Estado;
6 import java.io.IOException;
7 import java.sql.SQLException;
8 import jakarta.servlet.RequestDispatcher;
9 import jakarta.servlet.ServletException;
10 import jakarta.servlet.annotation.WebServlet;
11 import jakarta.servlet.http.HttpServlet;
12 import jakarta.servlet.http.HttpServletRequest;
13 import jakarta.servlet.http.HttpServletResponse;
14
15 /**
16 * Servlet para tratar Cidades.
146 CAPÍTULO 5. SISTEMA PARA CONTROLE DE CLIENTES
17 *
18 * @author Prof. Dr. David Buzatto
19 */
20 @WebServlet( name = "CidadesServlet",
21 urlPatterns = { "/processaCidades" } )
22 public class CidadesServlet extends HttpServlet {
23
24 protected void processRequest(
25 HttpServletRequest request,
26 HttpServletResponse response )
27 throws ServletException, IOException {
28
29 String acao = request.getParameter( "acao" );
30 CidadeDAO dao = null;
31 RequestDispatcher disp = null;
32
33 try {
34
35 dao = new CidadeDAO();
36
37 if ( acao.equals( "inserir" ) ) {
38
39 String nome = request.getParameter( "nome" );
40 int idEstado = Integer.parseInt(
41 request.getParameter( "idEstado" ) );
42
43 Estado e = new Estado();
44 e.setId( idEstado );
45
46 Cidade c = new Cidade();
47 c.setNome( nome );
48 c.setEstado( e );
49
50 dao.salvar( c );
51
52 disp = request.getRequestDispatcher(
53 "/formularios/cidades/listagem.jsp" );
54
55 } else if ( acao.equals( "alterar" ) ) {
56
57 int id = Integer.parseInt(request.getParameter( "id" ));
58 String nome = request.getParameter( "nome" );
5.5. CONSTRUINDO O SISTEMA 147
101 }
102
103 } catch ( SQLException exc ) {
104 exc.printStackTrace();
105 } finally {
106 if ( dao != null ) {
107 try {
108 dao.fecharConexao();
109 } catch ( SQLException exc ) {
110 exc.printStackTrace();
111 }
112 }
113 }
114
115 if ( disp != null ) {
116 disp.forward( request, response );
117 }
118
119 }
120
121 @Override
122 protected void doGet(
123 HttpServletRequest request,
124 HttpServletResponse response )
125 throws ServletException, IOException {
126 processRequest( request, response );
127 }
128
129 @Override
130 protected void doPost(
131 HttpServletRequest request,
132 HttpServletResponse response )
133 throws ServletException, IOException {
134 processRequest( request, response );
135 }
136
137 @Override
138 public String getServletInfo() {
139 return "CidadesServlet";
140 }
141
142 }
5.5. CONSTRUINDO O SISTEMA 149
40
41 <jsp:useBean
42 id="servicos"
43 scope="page"
44 class="cadastroclientes.servicos.EstadoServices"/>
45
46 <select name="idEstado" required>
47 <c:forEach items="${servicos.todos}" var="estado">
48 <c:choose>
49 <c:when test="${requestScope.cidade.estado.id eq
,→ estado.id}">
50 <option value="${estado.id}" selected>
51 ${estado.nome} - ${estado.sigla}
52 </option>
53 </c:when>
54 <c:otherwise>
55 <option value="${estado.id}">
56 ${estado.nome} - ${estado.sigla}
57 </option>
58 </c:otherwise>
59 </c:choose>
60 </c:forEach>
61 </select>
62
63 </td>
64 </tr>
65 <tr>
66 <td>
67 <a href="${cp}/formularios/cidades/listagem.jsp">Voltar</a>
68 </td>
69 <td class="alinharDireita">
70 <input type="submit" value="Alterar"/>
71 </td>
72 </tr>
73 </table>
74
75 </form>
76
77 </body>
78
79 </html>
5.5. CONSTRUINDO O SISTEMA 151
Na Listagem 5.23 temos algo muito interessante. Note que construímos nosso select
da mesma forma que fizemos na Listagem 5.21, entretanto precisamos saber qual é o
estado que é usado na cidade que será alterada. Para isso, ao usarmos o <c:forEach> ,
nós comparamos o id de cada estado do cadastro com o id do estado associado à
cidade que vai ser alterada. Caso os ids sejam iguais, isso quer dizer que é o estado
relacionado à cidade, sendo assim, a tag <option> é gerada com um atributo a mais,
o selected, que não possui valor. Usando esse atributo, nós dizemos ao navegador
que aquele item deve estar selecionado por padrão.
30 <tr>
31 <td class="alinharDireita">Estado:</td>
32 <td>${requestScope.cidade.estado.nome} -
,→ ${requestScope.cidade.estado.sigla}</td>
33 </tr>
34 <tr>
35 <td>
36 <a href="${cp}/formularios/cidades/listagem.jsp">Voltar</a>
37 </td>
38 <td class="alinharDireita">
39 <input type="submit" value="Excluir"/>
40 </td>
41 </tr>
42 </table>
43
44 </form>
45
46 </body>
47
48 </html>
1 package cadastroclientes.servicos;
2
3 import cadastroclientes.dao.ClienteDAO;
4 import cadastroclientes.entidades.Cliente;
5 import java.sql.SQLException;
6 import java.util.ArrayList;
7 import java.util.List;
8
9 /**
10 * Classe de serviços para a entidade Cliente.
11 *
12 * @author Prof. Dr. David Buzatto
13 */
14 public class ClienteServices {
15
16 /**
17 * Usa o ClienteDAO para obter todos os Clientes.
5.5. CONSTRUINDO O SISTEMA 153
18 *
19 * @return Lista de Clientes.
20 */
21 public List<Cliente> getTodos() {
22
23 List<Cliente> lista = new ArrayList<>();
24 ClienteDAO dao = null;
25
26 try {
27 dao = new ClienteDAO();
28 lista = dao.listarTodos();
29 } catch ( SQLException exc ) {
30 exc.printStackTrace();
31 } finally {
32 if ( dao != null ) {
33 try {
34 dao.fecharConexao();
35 } catch ( SQLException exc ) {
36 exc.printStackTrace();
37 }
38 }
39 }
40
41 return lista;
42
43 }
44
45 }
10 <meta charset="UTF-8">
11 <meta name="viewport"
12 content="width=device-width, initial-scale=1.0">
13 <link rel="stylesheet"
14 href="${cp}/css/estilos.css"/>
15 </head>
16
17 <body>
18
19 <h1>Clientes Cadastrados</h1>
20
21 <p>
22 <a href="${cp}/formularios/clientes/novo.jsp">
23 Novo Cliente
24 </a>
25 </p>
26
27 <table class="tabelaListagem">
28 <thead>
29 <tr>
30 <th>Id</th>
31 <th>Nome</th>
32 <th>Sobrenome</th>
33 <th>E-mail</th>
34 <th>CPF</th>
35 <th>Cidade</th>
36 <th>Alterar</th>
37 <th>Excluir</th>
38 </tr>
39 </thead>
40 <tbody>
41
42 <jsp:useBean
43 id="servicos"
44 scope="page"
45 class="cadastroclientes.servicos.ClienteServices"/>
46
47 <c:forEach items="${servicos.todos}" var="cliente">
48 <tr>
49 <td>${cliente.id}</td>
50 <td>${cliente.nome}</td>
51 <td>${cliente.sobrenome}</td>
5.5. CONSTRUINDO O SISTEMA 155
52 <td>${cliente.email}</td>
53 <td>${cliente.cpf}</td>
54 <td>${cliente.cidade.nome}</td>
55 <td>
56 <a href="${cp}/${prefixo}Alteracao&id=${cliente.id}">
57 Alterar
58 </a>
59 </td>
60 <td>
61 <a href="${cp}/${prefixo}Exclusao&id=${cliente.id}">
62 Excluir
63 </a>
64 </td>
65 </tr>
66 </c:forEach>
67 </tbody>
68
69 </table>
70
71 <p>
72 <a href="${cp}/formularios/clientes/novo.jsp">
73 Novo Cliente
74 </a>
75 </p>
76
77 <p><a href="${cp}/index.jsp">Tela Principal</a></p>
78
79 </body>
80
81 </html>
8 <title>Novo Cliente</title>
9 <meta charset="UTF-8">
10 <meta name="viewport"
11 content="width=device-width, initial-scale=1.0">
12 <link rel="stylesheet"
13 href="${cp}/css/estilos.css"/>
14 </head>
15
16 <body>
17
18 <h1>Novo Cliente</h1>
19
20 <form method="post" action="${cp}/processaClientes">
21
22 <input name="acao" type="hidden" value="inserir"/>
23
24 <table>
25 <tr>
26 <td class="alinharDireita">Nome:</td>
27 <td>
28 <input name="nome"
29 type="text"
30 size="20"
31 maxlength="45"
32 required/>
33 </td>
34 </tr>
35 <tr>
36 <td class="alinharDireita">Sobrenome:</td>
37 <td>
38 <input name="sobrenome"
39 type="text"
40 size="20"
41 maxlength="45"
42 required/>
43 </td>
44 </tr>
45 <tr>
46 <td class="alinharDireita">Data de Nascimento:</td>
47 <td>
48 <input name="dataNascimento"
49 type="date"
5.5. CONSTRUINDO O SISTEMA 157
50 size="8"
51 placeholder="dd/mm/yyyy"
52 required/>
53 </td>
54 </tr>
55 <tr>
56 <td class="alinharDireita">CPF:</td>
57 <td>
58 <input name="cpf"
59 type="text"
60 size="13"
61 pattern="\d{3}.\d{3}.\d{3}-\d{2}"
62 placeholder="999.999.999-99"
63 required/>
64 </td>
65 </tr>
66 <tr>
67 <td class="alinharDireita">E-mail:</td>
68 <td>
69 <input name="email"
70 type="email"
71 size="20"
72 maxlength="60"
73 required/>
74 </td>
75 </tr>
76 <tr>
77 <td class="alinharDireita">Logradouro:</td>
78 <td>
79 <input name="logradouro"
80 type="text"
81 size="25"
82 maxlength="50"
83 required/>
84 </td>
85 </tr>
86 <tr>
87 <td class="alinharDireita">Número:</td>
88 <td>
89 <input name="numero"
90 type="text"
91 size="6"
158 CAPÍTULO 5. SISTEMA PARA CONTROLE DE CLIENTES
92 maxlength="6"
93 required/>
94 </td>
95 </tr>
96 <tr>
97 <td class="alinharDireita">Bairro:</td>
98 <td>
99 <input name="bairro"
100 type="text"
101 size="15"
102 maxlength="30"
103 required/>
104 </td>
105 </tr>
106 <tr>
107 <td class="alinharDireita">CEP:</td>
108 <td>
109 <input name="cep"
110 type="text"
111 size="7"
112 pattern="\d{5}-\d{3}"
113 placeholder="99999-999"
114 required/>
115 </td>
116 </tr>
117 <tr>
118 <td class="alinharDireita">Cidade:</td>
119 <td>
120
121 <jsp:useBean
122 id="servicos"
123 scope="page"
124 class="cadastroclientes.servicos.CidadeServices"/>
125
126 <select name="idCidade" required>
127 <c:forEach items="${servicos.todos}" var="cidade">
128 <option value="${cidade.id}">
129 ${cidade.nome}
130 </option>
131 </c:forEach>
132 </select>
133
5.5. CONSTRUINDO O SISTEMA 159
134 </td>
135 </tr>
136 <tr>
137 <td>
138 <a href="${cp}/formularios/clientes/listagem.jsp">
139 Voltar
140 </a>
141 </td>
142 <td class="alinharDireita">
143 <input type="submit" value="Salvar"/>
144 </td>
145 </tr>
146 </table>
147
148 </form>
149
150 </body>
151
152 </html>
1 package cadastroclientes.controladores;
2
3 import cadastroclientes.dao.ClienteDAO;
160 CAPÍTULO 5. SISTEMA PARA CONTROLE DE CLIENTES
4 import cadastroclientes.entidades.Cidade;
5 import cadastroclientes.entidades.Cliente;
6 import java.io.IOException;
7 import java.sql.Date;
8 import java.sql.SQLException;
9 import java.time.LocalDate;
10 import java.time.format.DateTimeFormatter;
11 import jakarta.servlet.RequestDispatcher;
12 import jakarta.servlet.ServletException;
13 import jakarta.servlet.annotation.WebServlet;
14 import jakarta.servlet.http.HttpServlet;
15 import jakarta.servlet.http.HttpServletRequest;
16 import jakarta.servlet.http.HttpServletResponse;
17
18 /**
19 * Servlet para tratar Clientes.
20 *
21 * @author Prof. Dr. David Buzatto
22 */
23 @WebServlet( name = "ClientesServlet",
24 urlPatterns = { "/processaClientes" } )
25 public class ClientesServlet extends HttpServlet {
26
27 protected void processRequest(
28 HttpServletRequest request,
29 HttpServletResponse response )
30 throws ServletException, IOException {
31
32 String acao = request.getParameter( "acao" );
33
34 ClienteDAO dao = null;
35 RequestDispatcher disp = null;
36 DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
37
38 try {
39
40 dao = new ClienteDAO();
41
42 if ( acao.equals( "inserir" ) ) {
43
44 String nome = request.getParameter( "nome" );
45 String sobrenome = request.getParameter( "sobrenome" );
5.5. CONSTRUINDO O SISTEMA 161
Note que no início da Listagem 5.28 (linha 36), nós usamos a classe
java.time.format.DateTimeFormatter para converter a data inserida pelo usuá-
rio no formulário, que vem como texto, para um objeto do tipo java.sql.Date . Para
fazer essa conversão, precisamos criar o DateTimeFormatter com o formato que a
data chegará, no caso "yyyy-MM-dd" , ou seja, ano com quatro dígitos (yyyy), mês
com dois dígitos (MM) e dia com dois dígitos (dd). Com o formatador criado, usamos
o método parse(...) de java.time.LocalDate , sendo que a String com a data
que é passada para o método parse(...) deve estar no formato especificado no
construtor do DateTimeFormatter . Ainda, o objeto do tipo java.time.LocalDate
é usado como parâmetro do método valueOf(...) da classe java.sql.Date
para converter o java.time.LocalDate para java.sql.Date e configurar no ob-
jeto do tipo Cliente que está sendo populado com os dados apropriados.
11 <meta name="viewport"
12 content="width=device-width, initial-scale=1.0">
13 <link rel="stylesheet"
14 href="${cp}/css/estilos.css"/>
15 </head>
16
17 <body>
18
19 <h1>Alterar Cliente</h1>
20
21 <form method="post" action="${cp}/processaClientes">
22
23 <input name="acao" type="hidden" value="alterar"/>
24 <input name="id" type="hidden" value="${requestScope.cliente.id}"/>
25
26 <table>
27 <tr>
28 <td class="alinharDireita">Nome:</td>
29 <td>
30 <input name="nome"
31 type="text"
32 size="20"
33 maxlength="45"
34 required
35 value="${requestScope.cliente.nome}"/>
36 </td>
37 </tr>
38 <tr>
39 <td class="alinharDireita">Sobrenome:</td>
40 <td>
41 <input name="sobrenome"
42 type="text"
43 size="20"
44 maxlength="45"
45 required
46 value="${requestScope.cliente.sobrenome}"/>
47 </td>
48 </tr>
49 <tr>
50 <td class="alinharDireita">Data de Nascimento:</td>
51 <td>
52 <fmt:formatDate
166 CAPÍTULO 5. SISTEMA PARA CONTROLE DE CLIENTES
53 pattern="yyyy-MM-dd"
54 value="${requestScope.cliente.dataNascimento}"
55 var="data" scope="page"/>
56 <input name="dataNascimento"
57 type="date"
58 size="8"
59 placeholder="dd/mm/yyyy"
60 required
61 value="${data}"/>
62 </td>
63 </tr>
64 <tr>
65 <td class="alinharDireita">CPF:</td>
66 <td>
67 <input name="cpf"
68 type="text"
69 size="13"
70 pattern="\d{3}.\d{3}.\d{3}-\d{2}"
71 placeholder="999.999.999-99"
72 required
73 value="${requestScope.cliente.cpf}"/>
74 </td>
75 </tr>
76 <tr>
77 <td class="alinharDireita">E-mail:</td>
78 <td>
79 <input name="email"
80 type="email"
81 size="20"
82 maxlength="60"
83 required
84 value="${requestScope.cliente.email}"/>
85 </td>
86 </tr>
87 <tr>
88 <td class="alinharDireita">Logradouro:</td>
89 <td>
90 <input name="logradouro"
91 type="text"
92 size="25"
93 maxlength="50"
94 required
5.5. CONSTRUINDO O SISTEMA 167
95 value="${requestScope.cliente.logradouro}"/>
96 </td>
97 </tr>
98 <tr>
99 <td class="alinharDireita">Número:</td>
100 <td>
101 <input name="numero"
102 type="text"
103 size="6"
104 maxlength="6"
105 required
106 value="${requestScope.cliente.numero}"/>
107 </td>
108 </tr>
109 <tr>
110 <td class="alinharDireita">Bairro:</td>
111 <td>
112 <input name="bairro"
113 type="text"
114 size="15"
115 maxlength="30"
116 value="${requestScope.cliente.bairro}"/>
117 </td>
118 </tr>
119 <tr>
120 <td class="alinharDireita">CEP:</td>
121 <td>
122 <input name="cep"
123 type="text"
124 size="7"
125 pattern="\d{5}-\d{3}"
126 placeholder="99999-999"
127 required
128 value="${requestScope.cliente.cep}"/>
129 </td>
130 </tr>
131 <tr>
132 <td class="alinharDireita">Cidade:</td>
133 <td>
134
135 <jsp:useBean
136 id="servicos"
168 CAPÍTULO 5. SISTEMA PARA CONTROLE DE CLIENTES
137 scope="page"
138 class="cadastroclientes.servicos.CidadeServices"/>
139
140 <select name="idCidade" required>
141 <c:forEach items="${servicos.todos}" var="cidade">
142 <c:choose>
143 <c:when test="${requestScope.cliente.cidade.id eq
,→ cidade.id}">
144 <option value="${cidade.id}" selected>
145 ${cidade.nome}
146 </option>
147 </c:when>
148 <c:otherwise>
149 <option value="${cidade.id}">
150 ${cidade.nome}
151 </option>
152 </c:otherwise>
153 </c:choose>
154 </c:forEach>
155 </select>
156
157 </td>
158 </tr>
159 <tr>
160 <td>
161 <a href="${cp}/formularios/clientes/listagem.jsp">Voltar</a>
162 </td>
163 <td class="alinharDireita">
164 <input type="submit" value="Alterar"/>
165 </td>
166 </tr>
167 </table>
168
169 </form>
170
171 </body>
172
173 </html>
que a TagLib que contém essa tag é declarada no início da Listagem 5.29 usando o
prefixo fmt. Na tag <fmt:formatDate> , usamos o atributo pattern (padrão) para
configurarmos o formato da String que deve ser gerada a partir da data de nascimento
do cliente. O atributo value é usado para indicar o objeto da data que faz parte do
objeto cliente contido no requestScope, ou seja, o objeto que queremos formatar.
O atributo var é usado para indicar o nome da variável usada para armazenar o
resultado da formatação, sendo que no caso, configuramos o nome da variável como
data. Por fim, o atributo scope é usado para definir o escopo que essa variável vai
existir, no caso, cofiguramos para page, ou seja, a variável só vai existir dentro dessa
página. A seguir, como valor do input da data de nascimento, usamos a variável data,
obtida usando EL na forma ${data}.
27 <tr>
28 <td class="alinharDireita">Nome:</td>
29 <td>${requestScope.cliente.nome}</td>
30 </tr>
31 <tr>
32 <td class="alinharDireita">Sobrenome:</td>
33 <td>${requestScope.cliente.sobrenome}</td>
34 </tr>
35 <tr>
36 <td class="alinharDireita">Data de Nascimento:</td>
37 <td>
38 <fmt:formatDate
39 pattern="dd/MM/yyyy"
40 value="${requestScope.cliente.dataNascimento}"/>
41 </td>
42 </tr>
43 <tr>
44 <td class="alinharDireita">CPF:</td>
45 <td>${requestScope.cliente.cpf}</td>
46 </tr>
47 <tr>
48 <td class="alinharDireita">E-mail:</td>
49 <td>${requestScope.cliente.email}</td>
50 </tr>
51 <tr>
52 <td class="alinharDireita">Logradouro:</td>
53 <td>${requestScope.cliente.logradouro}</td>
54 </tr>
55 <tr>
56 <td class="alinharDireita">Número:</td>
57 <td>${requestScope.cliente.numero}</td>
58 </tr>
59 <tr>
60 <td class="alinharDireita">Bairro:</td>
61 <td>${requestScope.cliente.bairro}</td>
62 </tr>
63 <tr>
64 <td class="alinharDireita">CEP:</td>
65 <td>${requestScope.cliente.cep}</td>
66 </tr>
67 <tr>
68 <td class="alinharDireita">Cidade:</td>
5.6. RESUMO 171
69 <td>${requestScope.cliente.cidade.nome}</td>
70 </tr>
71 <tr>
72 <td>
73 <a href="${cp}/formularios/clientes/listagem.jsp">
74 Voltar
75 </a>
76 </td>
77 <td class="alinharDireita">
78 <input type="submit" value="Excluir"/>
79 </td>
80 </tr>
81 </table>
82
83 </form>
84
85 </body>
86
87 </html>
Veja que no final da Listagem 5.30 usamos novamente um formatador de datas (tag
<fmt:formatDate> ) para apresentar a data de nascimento do cliente. Note que
desta vez o uso do formatador foi simplificado, visto que agora não precisamos inserir
a data formatada dentro de um input como fizemos na Listagem 5.29. Quando quere-
mos apenas exibir a data formatada, basta usarmos a tag <fmt:formatDate> com
os atributos “pattern” e “value” configurados que a data formatada será gerada onde a
tag foi usada.
Ufa! Quanta coisa! Copiou todos os arquivos? Testou tudo o que você fez? Que bom!
Se tudo funcionou, parabéns! Caso tenha dado algum problema, verifique o que pode
ter acontecido, principalmente comparando o código que você copiou com o código
das listagens. Agora você é capaz de criar uma aplicação Web em Java que contenha
cadastros. Muito legal não é mesmo? Com essa bagagem teórica e prática que tivemos
neste e nos Capítulos anteriores, nós seremos capazes de trabalhar no projeto que
será proposto no Capítulo 6.
5.6 Resumo
Neste Capítulo construímos uma aplicação Web completa que mantém o cadastro de
Clientes, Cidades e Estados. Durante o nosso aprendizado, nós usamos os padrões
que aprendemos no Capítulo 4 para criar a arquitetura da nossa aplicação, dividindo
172 CAPÍTULO 5. SISTEMA PARA CONTROLE DE CLIENTES
5.7 Projetos
Projeto 5.1: Na listagem de clientes, insira uma nova coluna na tabela para apresentar
a data de nascimento de cada cliente. Use a tag <fmt:formatDate> para formatar
a data no formato dd/MM/yyyy (dia com dois dígitos, mês com dois dígitos e ano com
quatro dígitos). Não se esqueça de usar a diretiva <%@taglib ... %> para configurar
a TagLib de formatadores da JSTL.
Barão de Montesquieu
N
ESTE Capítulo aplicaremos o conhecimento adquirido até o momento na
construção de uma aplicação Web em Java.
6.1 Introdução
Neste Capítulo será apresentada uma série de requisitos que devem ser usados para
criar uma aplicação Web da mesma forma que fizemos no Capítulo 5. Tudo que será
requisitado estará baseado no que já aprendemos, sendo assim, todas as funcionali-
dades requeridas poderão ser implementadas com recursos já vistos no Capítulo 5.
Note que apesar do projeto ser intitulado como um sistema para locação de DVDs,
por enquanto a implementação da locação em si será deixada de lado, pois você a
fará no projeto do Capítulo 9. Isso se dá porque ainda precisamos aprender mais algu-
mas coisas, principalmente do lado do cliente, para sermos capazes de implementar
cadastros que lidem com relacionados muitos-para-muitos.
173
174 CAPÍTULO 6. PRIMEIRO PROJETO: SISTEMA PARA LOCAÇÃO DE DVDS V1.0
6.4 Resumo
Neste Capítulo foi requisitado que você implementasse uma aplicação Web em Java
para gerenciar o cadastro de DVDs de uma locadora (ainda sem a locação), por isso,
não há atividades a serem realizadas.
Parte II
175
CAPÍTULO
7
I NTRODUÇÃO À L INGUAGEM J AVA S CRIPT
Friedrich Nietzsche
N
ESTE Capítulo temos como objetivo aprender as construções básicas da
linguagem de programação JavaScript, vastamente utilizada no desen-
volvimento de aplicações Web.
7.1 Introdução
Chegamos a talvez à cereja do bolo, ou à cereja do livro ou então à cereja do desenvol-
vimento para Web: a linguagem de script JavaScript. A primeira coisa que precisamos
deixar claro é que Java e JavaScript são duas linguagens diferentes, com sintaxe base-
ada em C e com construções similares, mas o comum entre as duas para por aqui. A
linguagem JavaScript não é uma linguagem orientada a objetos, mas sim baseada em
protótipos. Nesse tipo de linguagem não existem classes, mas apenas objetos. Novos
objetos são criados a partir de cópias de objetos existentes. O JavaScript “moderno”,
baseado no padrão ECMAScript 2015 (sexta edição), possui algumas construções que
lembram àquelas de linguagens orientadas a objetos como classes e herança, mas
tudo isso é syntax sugar para simplificar coisas que já existiam anteriormente. Outras
177
178 CAPÍTULO 7. INTRODUÇÃO À LINGUAGEM JAVASCRIPT
| Saiba Mais
Quer conhecer um pouco mais da história e dos detalhes da linguagem JavaS-
cript? Veja o link <https://developer.mozilla.org/en-US/docs/Web/JavaScrip
t>.
| Saiba Mais
A referência da linguagem JavaScript pode ser acessada pelo link <https://deve
loper.mozilla.org/en-US/docs/Web/JavaScript/Reference>.
| Saiba Mais
As documentações e referências das tecnologias e APIs usadas para o desenvol-
vimento para Web podem ser acessadas pelo link <https://developer.mozilla.or
g/en-US/docs/Web/API>.
| Saiba Mais
Caso deseje fazer um tutorial completo sobre a linguagem, recomendo o ótimo
tutorial da própria MDN: <https://developer.mozilla.org/en-US/docs/Learn/J
avaScript>.
dos projetos do zero, mas a partir do próximo focarei apenas nas novidades que serão
apresentadas.
Vamos lá. Crie um projeto Java Web com o nome de “ExemplosEmJavaScript” da
forma que tem feito até aqui. Os passos descritos a seguir são somente estruturais.
Os respectivos códigos dos arquivos serão apresentados e explicados posteriormente.
Sendo assim, no nó Web Pages do projeto:
• Remova o arquivo index.html;
• Crie um JSP chamado index.jsp;
• Crie uma pasta chamada css;
• Crie uma pasta chamada js (JavaScript);
• Dentro da pasta css crie um arquivo CSS chamado estilos.css;
• Dentro da pasta js crie 12 arquivos JavaScript, chamados exemplo01.js, exemplo02.js...
exemplo12.js;
• Em Source Packages crie os pacotes exemplosemjavascript.pojo e
exemplosemjavascript.servlets;
• No pacote exemplosemjavascript.pojo crie uma classe chamada Pessoa;
• No pacote exemplosemjavascript.servlets crie os Servlets CalculaTabuadaServlet
e ListagemPessoasServlet;
• Importe e insira no projeto a biblioteca Jakarta EE Web 8 API;
• Clique com o botão direito do mouse no nó raiz do projeto e escolha o último
item do menu de contexto, chamado Properties ;
– Do lado esquerdo, em Categories: , clique no item CDNJS , situado den-
tro do nó JavaScript Libraries ;
– Do lado direito, clique no botão Add ;
– No diálogo que abriu, intitulado Add CDNJS Library , preencha o campo
Find: com “jquery” (sem as aspas) e clique em Search ;
– Após a busca na Content Delivery Network (CDN) aparecerão diversos
componentes na Graphical User Interface (GUI). Em Libraries: escolha
jquery, provavelmente o primeiro item;
– Em Files: marque a checkbox na frente do item jquery.min.js e clique
em Add Library como na Figura 7.1;
180 CAPÍTULO 7. INTRODUÇÃO À LINGUAGEM JAVASCRIPT
37 <button onclick="executarExemplo02(event)">
38 Exemplo 02 - Declarações de Variáveis e Suas Implicações
39 </button>
40 </p>
41
42 <p>
43 <button onclick="executarExemplo03(event)">
44 Exemplo 03 - Estruturas Condicionais e Operadores
45 </button>
46 </p>
47
48 <p>
49 <button onclick="executarExemplo04(event)">
50 Exemplo 04 - Estruturas de Repetição e Arrays
51 </button>
52 </p>
53
54 <p>
55 <button onclick="executarExemplo05(event)">
56 Exemplo 05 - "Classes" e Objetos e JSON
57 </button>
58 </p>
59 </div>
60
61 <hr>
62
63 <div>
64
65 <h1>Manipulação do DOM</h1>
66
67 <div>
68 <p>
69 <button onclick="executarExemplo06(event)">
70 Exemplo 06 - JavaScript Puro
71 </button>
72 </p>
73 <div id="divExemplo06" class="divExemplo"></div>
74 </div>
75
76 <div>
77 <p>
78 <button onclick="executarExemplo07(event)">
7.1. INTRODUÇÃO 185
205 </div>
206
207 <div>
208 <p>
209 <button onclick="executarExemplo12jQuery(event)">
210 Exemplo 12 - AJAX com jQuery e JSON
211 </button>
212 <button onclick="executarExemplo12Fetch(event)">
213 Exemplo 12 - AJAX com Fetch API e JSON
214 </button>
215 </p>
216 <div id="divExemplo12" class="divExemplo"></div>
217 </div>
218
219 </div>
220
221 </body>
222 </html>
14 border-style: solid;
15 border-radius: 5px;
16 height: 300px;
17 width: 600px;
18 overflow: scroll; /* se houver estouro, permitir rolagem */
19 overflow-style: scrollbar; /* mostrar barra de rolagem */
20 }
21
22 /* estilo para o seletor de classe .pDOM usando
23 * a pseudo-classe nth-child, indicando a seleção
24 * de todos os filhos pares
25 */
26 .pDOM:nth-child(even) {
27 background-color: #006699;
28 }
29
30 .canvasExemplo10 {
31 border: solid thin #000000;
32 margin: 10px; /* margin (margem) é o espaçamento externo da tag */
33 }
34
35 .dadosPessoa {
36 border: solid thin #006699;
37 background-color: #ffa800;
38 margin-bottom: 5px;
39 }
40
41 .dadosPessoa p {
42 margin: 0px;
43 }
44
45 /* seleção da primeira tag <p> filha do elemento
46 * que usar a classe .dadosPessoa
47 */
48 .dadosPessoa p:nth-child(1) {
49 background-color: #489eff;
50 }
51
52 /* seleção da segunda tag <p> filha do elemento
53 * que usar a classe .dadosPessoa
54 */
55 .dadosPessoa p:nth-child(2) {
190 CAPÍTULO 7. INTRODUÇÃO À LINGUAGEM JAVASCRIPT
56 background-color: #248bff;
57 }
58
59 /* já deu para entender, não é? */
60 .dadosPessoa p:nth-child(3) {
61 background-color: #0068dd;
62 }
| Saiba Mais
Sobre CSS, consulte <https://developer.mozilla.org/en-US/docs/Web/CSS> e
<https://developer.mozilla.org/en-US/docs/Web/CSS/Reference>.
1 package exemplosemjavascript.pojo;
2
3 import java.time.LocalDate;
4
5 /**
6 * Um Plain Old Java Object (POJO).
7 *
8 * @author Prof. Dr. David Buzatto
9 */
10 public class Pessoa {
11
12 private String nome;
13 private LocalDate dataNasc;
14 private double salario;
15
16 public String getNome() {
17 return nome;
18 }
19
20 public void setNome( String nome ) {
7.1. INTRODUÇÃO 191
21 this.nome = nome;
22 }
23
24 public LocalDate getDataNasc() {
25 return dataNasc;
26 }
27
28 public void setDataNasc( LocalDate dataNasc ) {
29 this.dataNasc = dataNasc;
30 }
31
32 public double getSalario() {
33 return salario;
34 }
35
36 public void setSalario( double salario ) {
37 this.salario = salario;
38 }
39
40 }
1 package exemplosemjavascript.servlets;
2
3 import java.io.IOException;
4 import java.io.PrintWriter;
5 import jakarta.servlet.ServletException;
6 import jakarta.servlet.annotation.WebServlet;
7 import jakarta.servlet.http.HttpServlet;
8 import jakarta.servlet.http.HttpServletRequest;
9 import jakarta.servlet.http.HttpServletResponse;
10
11 /**
192 CAPÍTULO 7. INTRODUÇÃO À LINGUAGEM JAVASCRIPT
54
55 @Override
56 protected void doPost(
57 HttpServletRequest request,
58 HttpServletResponse response )
59 throws ServletException, IOException {
60 processRequest( request, response );
61 }
62
63 @Override
64 public String getServletInfo() {
65 return "CalculaTabuadaServlet";
66 }
67
68 }
1 package exemplosemjavascript.servlets;
2
3 import exemplosemjavascript.pojo.Pessoa;
4 import java.io.IOException;
5 import java.io.PrintWriter;
6 import java.time.LocalDate;
7 import java.time.temporal.ChronoUnit;
8 import java.util.ArrayList;
9 import java.util.List;
10 import jakarta.json.bind.JsonbBuilder;
2
O formato JSON será tratado no exemplo 05. Por enquanto assuma que é uma forma de codificar
os dados de um objeto em forma de texto.
194 CAPÍTULO 7. INTRODUÇÃO À LINGUAGEM JAVASCRIPT
11 import jakarta.json.bind.Jsonb;
12 import jakarta.servlet.ServletException;
13 import jakarta.servlet.annotation.WebServlet;
14 import jakarta.servlet.http.HttpServlet;
15 import jakarta.servlet.http.HttpServletRequest;
16 import jakarta.servlet.http.HttpServletResponse;
17
18 /**
19 * Cria uma lista de pessoas, serializa em JSON e
20 * retorna ao cliente.
21 *
22 * @author Prof. Dr. David Buzatto
23 */
24 @WebServlet( name = "ListagemPessoasServlet",
25 urlPatterns = { "/listarPessoas" } )
26 public class ListagemPessoasServlet extends HttpServlet {
27
28 protected void processRequest(
29 HttpServletRequest request,
30 HttpServletResponse response )
31 throws ServletException, IOException {
32
33 response.setContentType( "application/json;charset=UTF-8" );
34
35 Jsonb jb = JsonbBuilder.create();
36 List<Pessoa> pessoas = new ArrayList<>();
37
38 int quantidade = Integer.parseInt(
39 request.getParameter( "quantidade" ) );
40
41 for ( int i = 1; i <= quantidade; i++ ) {
42
43 Pessoa p = new Pessoa();
44 p.setNome( String.format( "João da Silva %do", i ) );
45
46 LocalDate d = LocalDate.now();
47 d = d.plus( i, ChronoUnit.DAYS );
48 p.setDataNasc( d );
49
50 p.setSalario( 1000 * i );
51 pessoas.add( p );
52
7.2. FUNÇÕES DE E/S E OPERADORES ARITMÉTICOS 195
53 }
54
55 try ( PrintWriter out = response.getWriter() ) {
56 out.print( jb.toJson( pessoas ) );
57 }
58
59 }
60
61 @Override
62 protected void doGet(
63 HttpServletRequest request,
64 HttpServletResponse response )
65 throws ServletException, IOException {
66 processRequest( request, response );
67 }
68
69 @Override
70 protected void doPost(
71 HttpServletRequest request,
72 HttpServletResponse response )
73 throws ServletException, IOException {
74 processRequest( request, response );
75 }
76
77 @Override
78 public String getServletInfo() {
79 return "ListagemPessoasServlet";
80 }
81
82 }
Agora que temos toda a infraestrutura básica do nosso projeto, podemos começar a
falar sobre JavaScript. Vamos começar!
Na linha 5 é declarada uma variável local usando a palavra chave let 3 , com o
identificador n1 e atribuímos a ela o retorno da função prompt . Essa função recebe
como parâmetro uma String e, ao ser executada, apresenta ao usuário um diálogo com
uma mensagem (a String passada), um campo de texto, um botão de confirmação
e um de cancelamento. Ao se clicar no botão de confirmação o valor fornecido do
campo de texto será retornado ao chamador, no caso, atribuído à variável n1 e se
o diálogo for cancelado, será retornado o valor null . O retorno, quando válido, é
do tipo String. Note que não declaramos o tipo das variáveis em JavaScript, pois a
tipagem das variáveis é dinâmica, visto que o tipo de cada variável depende do valor
atribuído ou referenciado por ela.
Na linha 9 fazemos basicamente a mesma coisa para a variável n2 , mas o retorno
da função prompt é usada como argumento da função Number que converterá a
String retornada por prompt em um número e então esse valor será atribuído a n2 .
Entre as linhas 12 e 16 declaramos cinco novas variáveis e atribuímos a elas o resultado
de cinco operações. Note que como n1 referencia uma String, o operador + será
tratado como operador de concatenação de Strings ao invés de adição, ou seja, n2
será convertida para String e concatenada com n1 ! Como os outros operadores como
são aplicáveis apenas à números, n1 será convertido implicitamente e a operação
será realizada. O resultado disso será visto na saída que será gerada e exibida.
Falando da saída, na linha 19 declaramos a variável saida e concatenamos diversas
Strings para gerar o resultado. Em JavaScript existem três literais para Strings:
1. Delimitadas por aspas simples (apóstrofo): 'uma string' ;
2. Delimitadas por aspas duplas (aspas): "outra string" ;
3. Delimitadas por acento grave (crase): `mais uma string` ;
Os dois primeiros são análogos, com a diferença que quando se usa aspas simples
como delimitador e queremos ter uma aspas simples dentro da String, precisamos
escapá-la com contrabarra (barra invertida) e as aspas duplas não precisam. Por
exemplo, 'a\'b"c' corresponde à a'b"c . Quando delimitamos a String com aspas
duplas temos o contrário, ou seja, "a'b\"c" correspondendo à a'b"c .
O terceiro tipo de delimitador é mais interessante, pois permite que façamos a inter-
polação de valores dentro da String usando uma notação parecida com a da EL do
Java/Jakarta EE, mas que não tem relação a não ser a sintaxe similar. Para o nosso
exemplo, se n1 valer "10" e n2 valer 5 , o resultado de `${n1} e ${n2}` será
10 e 5 .
3
Veremos o propósito da palavra chave let no exemplo 02.
198 CAPÍTULO 7. INTRODUÇÃO À LINGUAGEM JAVASCRIPT
Por fim, para apresentar a String gerada, usamos duas formas. A primeira, na linha 29,
com a função alert que, assim como prompt , é bloqueante, fazendo a execução
do código parar naquele ponto ao esperar a interação do usuário. Essa função recebe
uma String como parâmetro e a mostra num diálogo ao ser executada. A outra forma
é usando a função log do objeto console , que recebe um ou vários objetos como
parâmetro e os mostram no console do navegador. No nosso exemplo, a exibição no
console está condicionada ao retorno da função confirm que exibe uma mensagem
ao usuário e aguarda a interação. Caso o usuário confirme a mensagem, a função
retornará um valor verdadeiro, usado na estrutura condicional if do exemplo.
Quando a palavra-chave let é usada, a variável só poderá ser usada depois da sua
inicialização, mesmo havendo hoisting para ela. O mesmo acontece com as constantes,
declaradas com const . Já as variáveis declaradas com a palavra-chave var serão
inicializadas com undefined . Por fim, as variáveis que são declaradas sem indicar
nenhuma dessas três palavras-chave passarão a existir no escopo global, o que pode
trazer uma série de problemas. Imagine que você declarou mais de uma variável com
o mesmo nome em dois ou mais escopos diferentes. A declaração de fato ocorrerá
quando o interpretador a encontrar pela primeira vez e, independende de onde for, ela
passará a existir no escopo global e a partir desse ponto você pode perder o controle
7.3. DECLARAÇÕES DE VARIÁVEIS E SUAS IMPLICAÇÕES 199
do valor que a variável referencia se não tomar muito cuidado com o que está fazendo.
O ideal é não utilizar ok?
O exemplo apresentado na Listagem 7.7 mostra todos esses efeitos quando for exe-
cutado. O “problema” da variável declarada sem let , var ou const pode ser
reproduzido ao se clicar pela segunda vez no botão do exemplo 02.
75 }
76
77 }
Veja o exemplo, todo o código está comentado, não sendo necessário entrar em mais
detalhes. Recomendo que você dê uma olhada nos links disponibilizados nas próximas
duas caixas “Saiba Mais”.
| Saiba Mais
Para uma explicação mais detalhada sobre essas implicações, acesse <http:
//www.constletvar.com/>.
| Saiba Mais
Para mais detalhes sobre declaração de variáveis em JavaScript, acesse <https://
developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements>.
Esse conceito pode gerar muita confusão, inclusive se declararmos uma variável
com var no contexto global (fora de funções) ela será uma variável global (uma
propriedade do objeto window), ao passo que dentro de uma função ela terá escopo
local, assim como let e const , mas inicializada como undefined . Ainda, é
importante frisar que uma constante tem ligação imutável (immutable binding) com
o que ela referencia, ou seja, ela não pode receber um novo valor, mas o objeto que
ela referencia pode ser modificado (ele não é imutável).
40 console.log( "não!" );
41 }
42
43 if ( v5 ) {
44 console.log( "não tbm!" );
45 }
46
47 if ( v6 ) {
48 console.log( "não tbm!" );
49 }
50
51 // NaN é um valor especial
52 if ( v6 == NaN ) {
53 console.log( "pq não?" );
54 }
55
56 if ( v6 === NaN ) {
57 console.log( "uai!?" );
58 }
59
60 if ( isNaN( v6 ) ) {
61 console.log( "pra NaN, só assim..." );
62 }
63
64 // operadores relacionais:
65 // igual: == (mesmo valor com conversão implícita)
66 // identidade: === (mesmo valor e mesmo tipo)
67 // diferente: != (valor diferente com conversão implícita)
68 // não identidade: !== (valor diferente e tipo diferente)
69 // menor: <
70 // menor ou igual: <=
71 // maior: >
72 // maior ou igual: >=
73
74 // operadores lógicos
75 // e lógico: &&
76 // ou lógico: ||
77 // não lógico: !
78
79 // veja a documentação referenciada no livro para mais detalhes
80
81 switch ( v1 ) {
204 CAPÍTULO 7. INTRODUÇÃO À LINGUAGEM JAVASCRIPT
82 case 1:
83 console.log( "v1 vale 1" );
84 break;
85 case 2:
86 console.log( "v1 vale 2" );
87 break;
88 default:
89 console.log( "v1 vale alguma coisa..." );
90 break;
91 }
92
93 // sem conversão automática!
94 switch ( v1 ) {
95 case "1":
96 console.log( "v1 vale 1" );
97 break;
98 case "2":
99 console.log( "v1 vale 2" );
100 break;
101 default:
102 console.log( "v1 vale alguma coisa..." );
103 break;
104 }
105
106 switch ( v2 ) {
107 case "1":
108 console.log( "v2 vale \"1\"" );
109 break;
110 case "2":
111 console.log( "v2 vale \"2\"" );
112 break;
113 default:
114 console.log( "v2 vale alguma coisa..." );
115 break;
116 }
117
118 }
7.5. ESTRUTURAS DE REPETIÇÃO E ARRAYS 205
| Saiba Mais
Para mais detalhes sobre os operadores em JavaScript, acesse <https://develo
per.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators>.
23
24 for ( let i = 0; i < a1.length; i++ ) {
25 console.log( `a1[${i}] = ${a1[i]}` );
26 }
27
28 // iterando usando o método forEach do
29 // objeto Array
30 a1.forEach( function( valor, indice ) {
31 console.log( `a1[${indice}] = ${valor}` );
32 });
33
34 for ( let i = 0; i < a2.length; i++ ) {
35 console.log( `a2[${i}] = ${a2[i]}` );
36 }
37
38 // notação de closure
39 a2.forEach( ( valor, indice ) => {
40 console.log( `a2[${indice}] = ${valor}` );
41 });
42
43 // a3 não tem elementos, pois o usamos como um
44 // "array associativo"
45 for ( let i = 0; i < a3.length; i++ ) {
46 // não entra aqui...
47 console.log( `a3[${i}] = ${a3[i]}` );
48 }
49
50 // e agora?
51 a3.forEach( valor => {
52 // não entra aqui também
53 console.log( valor );
54 });
55
56 // assim funciona, pois iteramos pelas
57 // propriedades do objeto
58 for ( let chave in a3 ) {
59 console.log( `a3[${chave}] = ${a3[chave]}` );
60 }
61
62 // ou
63 Object.keys( a3 ).forEach( chave => {
64 console.log( `a3[${chave}] = ${a3[chave]}` );
7.5. ESTRUTURAS DE REPETIÇÃO E ARRAYS 207
65 });
66
67
68 // métodos de iteração every e some.
69
70 // o método forEach não foi projetada parar
71 // parar no meio da execução. para isso, existem
72 // outras duas funções análogas que podem ser
73 // usadas para esse propósito.
74
75 // some => algum/alguns. a ideia é processar
76 // alguns elementos do array até que uma condição seja
77 // alcançada. o retorno true da função de callback faz
78 // a iteração parar e falso continuar para o próximo
79 // elemento.
80 let algum;
81 console.log( "há algum valor maior que 10?" );
82 algum = a1.some( maiorQue10 );
83 console.log( algum ? "sim" : "não" );
84
85 console.log( "há algum valor menor que 10?" );
86 algum = a1.some( menorQue10 );
87 console.log( algum ? "sim" : "não" );
88
89 // every => todos. a ideia é processar
90 // todos elementos do array verificando se todos
91 // passam por uma condição especificada na função
92 // de callback. o retorno true faz a função continuar
93 // para o próximo elemento, enquanto false a faz parar.
94 let todos;
95 console.log( "todos são maiores que 10?" );
96 todos = a1.every( maiorQue10 );
97 console.log( todos ? "sim" : "não" );
98
99 console.log( "todos são menores que 10?" );
100 todos = a1.every( menorQue10 );
101 console.log( todos ? "sim" : "não" );
102
103 // while e do while são análogos a C, C++, Java etc.
104
105 }
106
208 CAPÍTULO 7. INTRODUÇÃO À LINGUAGEM JAVASCRIPT
| Saiba Mais
Para mais detalhes sobre o tipo Array em JavaScript, acesse <https://developer.
mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array>.
coisa acontece entre as linhas 34 e 41, com a diferença de se usar a notação de closure
para a definição da função de callback utilizada no método forEach de a2 .
Além do método forEach existem alguns outros para o processamento dos elemen-
tos de um array. Dois desses métodos são mostrados a partir da linha 80: every e
some . Como os nomes sugerem, o primeiro é utilizado com a premissa de testar
alguma condição em todos os elementos do array, enquanto o outro espera-se que
algum elemento não se enquadre em algo desejado. Podemos utilizar esses métodos
para, por exemplo, executar uma busca/pesquisa sequencial/linear no array e parar a
iteração quando o elemento for encontrado, retornando um valor verdadeiro ou falso,
dependendo da situação e do método empregado. Veja os comentários do exemplo e
teste a execução do código para entender exatamente do que se trata.
1 class Forma {
2
3 // só pode haver um construtor
4 constructor( xIni, yIni, xFim, yFim ) {
5 // criação da propriedade
210 CAPÍTULO 7. INTRODUÇÃO À LINGUAGEM JAVASCRIPT
6 // usando this
7 this.xIni = xIni;
8 this.yIni = yIni;
9 this.xFim = xFim;
10 this.yFim = yFim;
11 }
12
13 calcularArea() {
14 return 0;
15 }
16
17 }
18
19 // "herança"
20 class Retangulo extends Forma {
21
22 // sobrescrevendo a função calcularArea
23 calcularArea() {
24 let largura = Math.abs( this.xFim - this.xIni );
25 let altura = Math.abs( this.yFim - this.yIni );
26 return largura * altura;
27 }
28
29 }
30
31 class Circulo extends Forma {
32
33 // novo construtor
34 constructor( xCentro, yCentro, raio ) {
35 super( xCentro, yCentro, 0, 0 );
36 this.raio = raio;
37 }
38
39 calcularArea() {
40 return Math.PI * this.raio * this.raio;
41 }
42
43 }
44
45 function executarExemplo05( event ) {
46
47 let r = new Retangulo( 0, 0, 10, 20 );
7.6. “CLASSES”, OBJETOS E JSON 211
| Saiba Mais
Para saber mais sobre classes em JavaScript, acesse <https://developer.mozilla.
org/en-US/docs/Web/JavaScript/Reference/Classes>.
| Saiba Mais
Para consultar a documentação sobre o tipo String em JavaScript, acesse <https:
//developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Obj
ects/String>.
| Saiba Mais
Para consultar a documentação sobre JSON em JavaScript, acesse <https://de
veloper.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects
/JSON>.
| Saiba Mais
A documentação do DOM pode ser vista no link <https://developer.mozilla.or
g/en-US/docs/Web/API/Document_Object_Model>.
Vamos manipular o DOM de duas formas, uma usando JavaScript puro, que normal-
mente é mais verboso, e a outra usando a biblioteca jQuery4 , que já foi padrão na
construção de aplicações para Web e tem caído em desuso, mas ainda é relevante, prin-
cipalmente por facilitar algumas coisas e ainda estar presente de forma consistente
em diversas aplicações criadas e que você provavelmente terá que dar manutenção
na sua vida profissional.
1 var contadorExemplo06 = 1;
2
3 function executarExemplo06( event ) {
4
5 // obtém o elemento pelo identificador
6 let div = document.getElementById( "divExemplo06" );
7
8 // cria um novo elemento do tipo p (tag <p>)
9 let p = document.createElement( "p" );
10
11 // configura os atributos
12 p.innerHTML = `JS Puro - Contador: ${contadorExemplo06++}`;
13 p.className = "pDOM";
14
15 // adiciona na div
16 div.append( p );
17
18 }
1 var contadorExemplo07 = 1;
2
3 function executarExemplo07( event ) {
4
5 // seleciona a div com id divExemplo07
6 // a jQuery usa a sintaxe os seletores do CSS
7 let div = $( "#divExemplo07" );
8
9 // cria um novo elemento do tipo p (tag <p>)
10 // e configura os atributos encadeando a chamada de métodos
11 let p = $( "<p></p>" )
12 .html( `jQuery - Contador: ${contadorExemplo07++}` )
13 .addClass( "pDOM" );
14
15 // adiciona na div
16 div.append( p );
17
18 }
| Saiba Mais
Se quiser aprender um pouco mais sobre a jQuery, acesse <https://learn.jquery
.com/>.
| Saiba Mais
1 let contadorOpSelect03 = 4;
2 let contadorOpSelect04 = 4;
3
4 function lerDadosFormulario( event ) {
5
6 // obtém os dados do formulário usando
7 // JavaCcript puro
8
9 // obtém por id (não deve haver mais de um!)
10 // e pegar a propriedade value
11 let campo01 = document.getElementById( "campo01" ).value;
12
13 // obtém pelo atributo name (pode haver mais de um!)
14 // e na seleção, usa o primeiro elemento do resultado
15 let campo02 = document.getElementsByName( "campo02" )[0].value;
16
17 // por id
18 let select03 = document.getElementById( "select03" ).value;
19 let select04 = document.getElementById( "select04" ).value;
20 let area05 = document.getElementById( "area05" ).value;
21
22 alert(
23 `Campo 01: ${campo01}\n` +
24 `Campo 02: ${campo02}\n` +
25 `Select 03: ${select03}\n` +
26 `Select 04: ${select04}\n` +
27 `Área 05: ${area05}` );
28
218 CAPÍTULO 7. INTRODUÇÃO À LINGUAGEM JAVASCRIPT
29 }
30
31 // funções podem ser atribuídas à variáveis!
32 var lerDadosFormularioJQuery = function( event ) {
33
34 // com a jQuery, usa-se a sintaxe igual
35 // aos seletores do CSS
36
37 // a função val() retornará o valor
38 // do componente de forma padronizada
39 let campo01 = $( "#campo01" ).val();
40 let campo02 = $( "#campo02" ).val();
41 let select03 = $( "#select03" ).val();
42 let select04 = $( "#select04" ).val();
43 let area05 = $( "#area05" ).val();
44
45 alert(
46 `Campo 01: ${campo01}\n` +
47 `Campo 02: ${campo02}\n` +
48 `Select 03: ${select03}\n` +
49 `Select 04: ${select04}\n` +
50 `Área 05: ${area05}` );
51
52 }; // termina com ponto e vírgula
53
54 // pode-se usar a sintaxe de closures
55 let inserirDadosFormulario = event => {
56
57 // configurando os valores
58 document.getElementById( "campo01" ).value = "campo 01 atualizado";
59 document.getElementsByName( "campo02" )[0].value = "campo 02 também";
60 document.getElementById( "select03" ).value = "o2";
61 document.getElementById( "select04" ).value = "o3";
62 document.getElementById( "area05" ).value = "outro valor";
63
64 }; // termina com ponto e vírgula
65
66 function inserirDadosFormularioJQuery( event ) {
67
68 // configura os valores usando a função val()
69 $( "#campo01" ).val( "novo valor campo 01" );
70 $( "#campo02" ).val( "outro novo valor... ");
7.8. MANIPULAÇÃO DE FORMULÁRIOS 219
Neste exemplo são definidas seis funções que tratarão o evento click de seis botões. A
ideia é apresentar três situações de duas formas diferentes, uma com JavaScript puro e
uma usando jQuery. Na linha 11 da primeira função, lerDadosFormulario(event) ,
obtemos o valor do campo de texto identificado por campo01 em seu id. Veja que
obtemos o elemento pelo id (método getElementById( "id" ) ), acessamos a
propriedade value e atribuímos à variável campo01 . Podemos também obter com-
220 CAPÍTULO 7. INTRODUÇÃO À LINGUAGEM JAVASCRIPT
ponentes pelo atributo name, entretanto pode haver mais de um componente com o
mesmo name, então o método getElementsByName( "name" ) retorna um array.
No nosso exemplo há apenas um componente com o nome campo02, mas como o
retorno do método getElementsByName( "name" ) é um array, precisamos obter
o primeiro elemento do array retornado e então obter o valor.
Entre as linhas 18 e 20 obtemos o valor de dois componentes <select> e um
<textarea> . Note que para os selects o valor retornado será o da opção selecio-
nada no momento. Por fim, entre as linhas 22 e 27, apresentamos os valores lidos
usando um alerta.
A segunda função implementada, lerDadosFormularioJQuery(event) , faz a mesma
coisa que a primeira, entretanto usando jQuery. Perceba que o código fica mais limpo
e enxuto. Basta usar o seletor de id do CSS para obter o componente desejado e usar
o método val() para obter o valor.
7.9 Eventos
Quase todas as tags HTML disparam uma vasta quantidade de tipos de eventos que
podem ser tratados. Claro que a maioria dos eventos “úteis” são ouvidos em com-
ponentes visíveis, mas há inúmeras possibilidades. Na Listagem 7.14 é apresentado
como se faz programaticamente o registro de tratadores de eventos em nós do DOM
ao invés de fazer isso direto no HTML como estamos fazendo com os botões.
1 function registrarEventosExemplo09() {
2
3 // JavaScript puro
7.9. EVENTOS 221
É importante perceber que essa função precisa ser executada para que os tratadores
de eventos sejam registrados de fato, sendo assim, na linha 171 da Listagem 7.1, essa
função é invocada dentro da tag <script> . Normalmente quando se quer registrar
eventos programaticamente no documento isso é feito quando todo o documento
está pronto para ser processado, ou seja, todo o DOM foi construído. Veremos isso na
prática no Capítulo 8.
Usando JavaScript puro, o registro de eventos é feito associando às propriedades
prefixadas com on dos nós do DOM uma função para o tratamento dos mesmos. Na
linha 4 da Listagem 7.14 pode-se ver a obtenção de um campo de texto e o registro
do tratador de evento para o evento “key down” (tecla pressionada) que mostrará, via
console.log(...) , o caractere digitado no campo de texto.
Já na linha 9 fazemos o registro do evento change de um select usando jQuery. Veja
como a sintaxe é diferente. O evento change é disparado quando se seleciona uma
opção do select em questão, desde que a opção seja diferente da opção selecionada
no momento. No nosso caso, o valor da opção selecionada será mostrada usando um
alerta.
Na caixa “Saiba Mais” abaixo você poderá conferir uma lista completa de todos os
eventos que existem e podem ser usados.
| Saiba Mais
A documentação completa sobre os eventos que podem ser tratados pode ser
vista em <https://developer.mozilla.org/en-US/docs/Web/Events>.
222 CAPÍTULO 7. INTRODUÇÃO À LINGUAGEM JAVASCRIPT
Nesta Seção falaremos um pouco sobre o tag <canvas> e sobre a Canvas API, que
nos permitirá realizar operações de desenho em Duas Dimensões (2D). Atualmente,
além de trabalhar com desenhos em duas dimensões, podemos também usar a WebGL
API para realizar desenhos em 2D ou Três Dimensões (3D) usando aceleração em
hardware, mas isso foge demais do escopo deste livro. Focaremos no 2D realizando a
simulação da física de “bolinhas” que serão animadas para que você possa conhecer
um pouco sobre a Canvas API e sobre a utilização da função setInterval(...)
que nos permetirá executar código repetidamente em um intervalo predefinido de
tempo. Na Listagem 7.15 podemos ver o código completo do exemplo.
1 function prepararCanvasExemplo10() {
2
3 class Bolinha {
4
5 // x e y são os centros da bolinha
6 constructor( x, y, velocidadeX, velocidadeY, raio, cor ) {
7 this.x = x;
8 this.y = y;
9 this.raio = raio;
10 this.cor = cor;
11 this.velocidadeX = velocidadeX;
12 this.velocidadeY = velocidadeY;
13 this.elasticidade = 0.9; // elasticidade do material
14 this.atrito = 0.99; // atrito do material com o meio
15 this.emArraste = false; // está sendo arrastada?
16 }
17
18 desenhar( ctx ) {
19
20 // cor usada pelo contexto gráfico
21 // para realizar o desenho
22 ctx.fillStyle = this.cor;
23
24 // inciando um caminho
25 ctx.beginPath();
26
7.10. SIMULAÇÃO USANDO A CANVAS API 223
69
70 }
71
72 // se a bolinha passou do "chão" do <canvas>
73 if ( this.y + this.raio > alturaCanvas ) {
74
75 // reposiciona a bolinha sem passar da fronteira
76 this.y = alturaCanvas - this.raio;
77
78 // recalcula a velocidade em y, trocando a direção
79 // e aplicando a elasticidade
80 this.velocidadeY = -this.velocidadeY * this.elasticidade;
81
82 }
83
84 // se a bolinha passou do "teto" do <canvas>
85 if ( this.y - this.raio < 0 ) {
86
87 // reposiciona a bolinha sem passar da fronteira
88 this.y = this.raio;
89
90 // recalcula a velocidade em y, trocando a direção
91 // e aplicando a elasticidade
92 this.velocidadeY = -this.velocidadeY * this.elasticidade;
93
94 }
95
96 // aplica o atrito
97 this.velocidadeX *= this.atrito;
98 this.velocidadeY *= this.atrito;
99
100 // aplica a gravidade
101 this.velocidadeY += gravidade;
102
103 }
104
105 }
106
107 // a coordenada x, y está dentro da bolinha
108 intercepta( x, y ) {
109
110 // pitágoras ;)
7.10. SIMULAÇÃO USANDO A CANVAS API 225
153 2.0,
154 10,
155 "rgb(0,0,0)" );
156
157 // qual bolinha está sendo arrastada?
158 let bolinhaEmArraste = null;
159
160 // cria um array de bolinhas
161 let bolinhas = [ bolinha ];
162
163 // "engine/motor" da simulação
164 // a função passada será executada a cada
165 // 10 milisegundos, ou seja cada segundo terá
166 // aproximadamente 100 quadros de animação
167 setInterval( () => {
168
169 // limpa os desenhos anteriores
170 context.clearRect( 0, 0, larguraCanvas, alturaCanvas );
171
172 // realiza a movimentação e o desenho de cada
173 // bolinha
174 bolinhas.forEach( bolinha => {
175 bolinha.mover();
176 bolinha.desenhar( context );
177 });
178
179 }, 10 );
180
181
182 /*******************************
183 * interação com o usuário *
184 *******************************/
185
186 // quando algum botão do mouse for pressionado no <canvas>
187 canvas.onmousedown = event => {
188
189 // se for o botão esquerdo
190 if ( event.button === 0 ) {
191
192 // verifica se a posição que foi clicada
193 // está em cima de alguma bolinha
194 // percorre-se o array ao contrário para dar
7.10. SIMULAÇÃO USANDO A CANVAS API 227
279 };
280
281 // o cursor do mouse moveu dentro do canvas
282 canvas.onmousemove = event => {
283
284 // há bolinha em arraste?
285 if ( bolinhaEmArraste !== null ) {
286
287 // obtém as coordenadas anteriores
288 xAntigo = bolinhaEmArraste.x;
289 yAntigo = bolinhaEmArraste.y;
290
291 // reposiciona a bolinha de acordo com
292 // a difença calculada no clique/seleção
293 bolinhaEmArraste.x = event.offsetX - dx;
294 bolinhaEmArraste.y = event.offsetY - dy;
295
296 // recalcula as velocidades para
297 // quando essa bolinha for solta
298 // dando a impressão de arremesso
299 bolinhaEmArraste.velocidadeX = ( bolinhaEmArraste.x - xAntigo ) /
,→ 2;
300 bolinhaEmArraste.velocidadeY = ( bolinhaEmArraste.y - yAntigo ) /
,→ 2;
301
302 }
303
304 };
305
306 // esconde o menu de contexto no clique
307 // com o botão direito
308 canvas.oncontextmenu = event => {
309 // não realiza a ação padrão
310 // nesse caso, não exibir o menu de contexto
311 event.preventDefault();
312 };
313
314 }
315
316 // cria uma cor aleatória
317 function gerarCorAleatoria() {
318
230 CAPÍTULO 7. INTRODUÇÃO À LINGUAGEM JAVASCRIPT
Entre as linhas 3 e 121 definimos uma classe chamada Bolinha. A ideia é que essa
classe modele uma bolinha, na verdade um círculo pequeno, que estará suscetível à
forças físicas que simularemos. O construtor da classe, iniciado na linha 6, possui seis
parâmetros:
• x : é a posição do centro da bolinha no eixo x;
• y : é a posição do centro da bolinha no eixo y;
• velocidadeX : é a quantidade de pixels que a bolinha vai ser movida no eixo x
a cada passo da simulação;
• velocidadeY : é a quantidade de pixels que a bolinha vai ser movida no eixo y
a cada passo da simulação;
• raio : representa o raio do círculo;
• cor : é a cor que será utilizada para desenhar a bolinha.
Ao construir a bolinha esses valores serão usados para inicializar os atributos corres-
pondentes e, além desses, outros atributos serão criados com valores fixos:
• elasticidade : representa algo como o coeficiente de elasticidade do “mate-
rial” da bolinha. Na nossa simulação ele será fixo para todas as bolinhas criadas
e será usado quando uma bolinha bater em alguma parede;
• atrito : é a tentativa de simular o coeficiente de atrito do material da bolinha
com o meio em que ela está imersa;
• emArraste : indica se a bolinha está sendo arrastada na simulação, ou seja, se
o usuário a “pegou” com o mouse e a está arrastando no <canvas> .
7.10. SIMULAÇÃO USANDO A CANVAS API 231
5
Costumeiramente em APIs gráficas, o sistema de coordenadas cartesianas em um plano é centrali-
zado no canto superior esquerdo dos componentes gráficos, ou seja, a origem de ambos os eixos, o
ponto {0, 0}, está situado nesse canto, com o eixo x crescendo para a direita e o eixo y crescendo para
baixo, ou seja, para o eixo y temos o contrário do que estamos acostumados na matemática.
232 CAPÍTULO 7. INTRODUÇÃO À LINGUAGEM JAVASCRIPT
| Saiba Mais
A API do Canvas pode ser vista em <https://developer.mozilla.org/en-US/docs
/Web/API/Canvas_API>.
| Saiba Mais
Mais sobre Promises: <https://developer.mozilla.org/pt-BR/docs/Web/JavaScr
ipt/Reference/Global_Objects/Promise>
| Saiba Mais
Mais sobre a API Web Workers: <https://developer.mozilla.org/en-US/docs/W
eb/API/Web_Workers_APII>.
40
41 // cria um objeto do tipo URLSearchParams
42 // que encapsula os parâmetros enviados
43 // pela requisição assíncrona da função
44 // fetch
45 let parametros = new URLSearchParams();
46 parametros.append( "numero", n );
47
48 // envia uma requisição à URL "calcularTabuada" e passa
49 // um init object com os atributos method e body
50 fetch( "calcularTabuada", {
51 method: "POST",
52 body: parametros
53
54 // se bem sucedido, retorna o texto da resposta
55 }).then( response => {
56 return response.text();
57
58 // encadeia com o then anterior, tratando o texto
59 // retornado
60 }).then( text => {
61 $( "#divExemplo11" ).html( text );
62
63 // se algum problema ocorrer...
64 }).catch( error => {
65 alert( "Erro: " + error );
66 });
67
68 }
As duas funções obtém um valor, que se espera que seja um número inteiro, pois
não há tratamento, e então o usam como parâmetro em uma requisição ao Servlet
mapeado na URL /calcularTabuada. Como o script executará no mesmo diretório
em que o Servlet está mapeado, não há necessidade de informar o caminho da URL
da requisição de forma absoluta.
Como o código está totalmente comentado não ficarei explicando-o detalhadamente,
mas a ideia é que quando a requisição for enviada ela será tratada pelas funções
de callback apropriadas dependendo do que acontecer. Vejam, é uma promessa do
tipo “vou enviar algo para o servidor pedindo algum recurso e esperar que ele me
responda algo”. Essa resposta pode acontecer em um milésimo de segundo ou em
alguns segundos ou mesmo nunca retornar! Então espera-se que no futuro o retorno
236 CAPÍTULO 7. INTRODUÇÃO À LINGUAGEM JAVASCRIPT
(ou não retorno!) da requisição seja tratado. O que diferencia as chamadas assíncronas
nos dois exemplos é justamente a sintaxe das funções. Ambas têm como primeiro
parâmetro a URL que deve ser alcançada e como segundo um objeto de opções, que
variará de acordo com a função utilizada. Na ajax(...) criaremos um objeto com
as propriedades data, que contém os parâmetros que serão codificados na requisição
e dataType que informa ao chamador que tipo de dado será retornado pelo servidor.
Já na fetch(...) os parâmetros devem ser encapsulados em um objeto do tipo
URLSearchParams e configurados na propriedade body do objeto de opções, que na
Fetch API é chamado de objeto de inicialização (init object). Em ambas as funções,
caso o método HTTP que deve ser utilizado não for informado, o padrão será utilizar
GET.
Recomendo a leitura das respectivas documentações fornecidas nas três caixas “Saiba
Mais” abaixo.
| Saiba Mais
Documentação da função jQuery.ajax(): <https://api.jquery.com/jquery.ajax/>.
| Saiba Mais
Documentação da Fetch API: <https://developer.mozilla.org/en-US/docs/Web
/API/Fetch_API>.
| Saiba Mais
Usando a Fetch API: <https://developer.mozilla.org/en-US/docs/Web/API/Fet
ch_API/Using_Fetch>.
Nosso último exemplo é parecido com o primeiro, com a diferença que agora o servi-
dor, ao invés de responder texto puro, nos enviará dados em JSON. Sim, JSON é texto
puro também, mas como o servidor informará que o texto que está vindo é em JSON,
as funções conseguirão fazer a desserialização dos dados de forma automática para
que possamos tratá-los por causa do cabeçalho da resposa. Na Listagem 7.17 podem
ser vistas as definições das duas funções, novamente uma usando jQuery e a outra a
Fetch API.
7.11. REQUISIÇÕES ASSÍNCRONAS E INTERCÂMBIO DE DADOS 237
39 fetch( "listarPessoas", {
40 method: "POST",
41 body: parametros
42 }).then( response => {
43 // faz o parse do json em objeto e retorna
44 return response.json();
45 }).then( data => {
46
47 let $div = $( "#divExemplo12" );
48 $div.html( "" );
49
50 data.forEach( pessoa => {
51 $div.append(
52 `<div class="dadosPessoa">Pessoa:<p>Nome:
,→ ${pessoa.nome}</p>` +
53 `<p>Data de Nascimento: ${pessoa.dataNasc}</p>` +
54 `<p>Salário: R$ ${pessoa.salario}</p></div>` );
55 });
56
57 }).catch( error => {
58 alert( "Erro: " + error );
59 });
60
61 }
7.12 Resumo
Chegamos ao fim de mais um Capítulo, onde aprendemos o básico sobre JavaScript
que é a linguagem que, invariavelmente, qualquer desenvolvedor Web terá que lidar
no seu trabalho. É importante que você tenha um certo domínio sobre a mesma e
que, após a leitura e entendimento do Capítulo, você seja capaz de continuar seu
aprendizado. No próximo Capítulo usaremos JavaScript para implementarmos um
formulário de venda de produtos, com a definição da quantidade que certo produto
será vendido. Usaremos também AJAX para podermos cancelar uma venda. Enfim,
isso é assunto para o próximo Capítulo!
7.13. PROJETOS 239
7.13 Projetos
Projeto 7.2: Repita o Projeto 7.1, só que agora para a tabela “carro”. Um carro deve
ter um identificador, um nome ( VARCHAR ), um modelo ( VARCHAR ) e um ano de
fabricação ( INT ). Note que esse projeto é parecido com o Projeto 4.2 do Capítulo 4.
Projeto 7.3: Repita o Projeto 7.1, só que agora para a tabela “produto”. Um produto
deve ter um identificador, uma descrição ( VARCHAR ) e uma quantidade em estoque
( INT ). Note que esse projeto é parecido com o Projeto 4.3 do Capítulo 4.
CAPÍTULO
8
S ISTEMA PARA V ENDA DE P RODUTOS
Albert Einstein
N
ESTE Capítulo iremos construir mais um projeto completo, concluindo
o aprendizado dos conhecimentos básicos para que você possa cons-
truir praticamente qualquer tipo de sistema que lide com bancos de
dados relacionais, ou seja, aprenderemos a lidar com a implementação
de cadastros que têm relacionamentos muitos-para-muitos.
8.1 Introdução
Finalmente estamos aptos a construir uma aplicação Web em Java com a maioria das
funcionalidades necessárias à maioria das aplicações Web que você desenvolverá na
sua vida profissional. Iremos incrementar a aplicação criada no Capítulo 5 desenvol-
vendo mais alguns cadastros e amarrando todos eles em um cadastro de vendas de
produtos. Vamos lá!
Além de clientes, cidades e estados, criaremos mais três cadastros com inserção,
alteração e remoção de registros: unidades de medida, produtos e fornecedores. Com
esses seis cadastros desenvolveremos a interface gráfica das vendas, onde poderemos
gerar novas vendas e que, após serem feitas, poderão ser canceladas.
241
242 CAPÍTULO 8. SISTEMA PARA VENDA DE PRODUTOS
Na Figura 8.1 pode ser visto o DER da base de dados venda_produtos. Note que o
nome da base é diferente do projeto anterior. Para esse projeto o script SQL para a
criação da base de dados não será fornecido pois, além da estrutura, teremos algumas
inserções já prontas para podermos testar. Para gerar a base, com o MariaDB/MySQL
em execução, abra o modelo da base no MySQL Workbench, disponibilizado nos
arquivos do Capítulo. Com o modelo aberto, clique no menu Database escolha
a opção Forward Engineer... e siga o assistente. A base de dados, as tabelas e os
relacionamentos serão criados, além de várias inserções para as tabelas estado, cidade,
cliente, fornecedor, unidade_medida e produto que serão realizadas.
O diagrama de classes das entidades do projeto pode ser visto Figura 8.2. Veja que a
classe ItemVenda é a classe que fará o papel de viabilizar o relacionamento muitos-
para-muitos entre produtos e vendas. Note que na UML existe a notação de classe
associativa que poderia ter sido usada para representar esse relacionamento.
8.2. CONSTRUINDO O SISTEMA 243
Para que esse Capítulo não fique gigantesco, focaremos apenas nas novidades ou
modificações que foram feitas em relação ao projeto anterior. Você poderá pegar o
projeto pronto nos arquivos do Capítulo. A entidade Produto será usada como base
para entendermos o que foi alterado e depois toda a parte da venda propriamente
dita será detalhada com mais afinco.
e precisamos tomar muito, muito, MUITO cuidado com esse tipo de dado em um
sistema de verdade. Além disso, cada atributo será anotado com pelo menos uma
anotação de validação, pois todos os nossos objetos das entidades serão validados,
obrigatoriamente antes de serem submetidos aos seus respectivos DAOs, filtrando
possíveis tentativas de adulteração dos dados submetidos através dos formulários. Na
entidade Produto, apresentada na Listagem 8.1, podemos ver isso.
1 package vendaprodutos.entidades;
2
3 import java.math.BigDecimal;
4 import jakarta.validation.constraints.NotNull;
5 import jakarta.validation.constraints.Pattern;
6 import jakarta.validation.constraints.PositiveOrZero;
7 import jakarta.validation.constraints.Size;
8
9 /**
10 * Entidade Produto.
11 *
12 * @author Prof. Dr. David Buzatto
13 */
14 public class Produto {
15
16 @NotNull
17 private Long id;
18
19 @NotNull
20 @Size( min = 1, max = 60 )
21 private String descricao;
22
23 @NotNull
24 @Pattern( regexp = "^\\d{13}$",
25 message = "deve corresponder à 9999999999999" )
26 private String codigoBarras;
27
28 @NotNull
29 @PositiveOrZero
30 private BigDecimal valorVenda;
31
32 @NotNull
8.2. CONSTRUINDO O SISTEMA 245
75 }
76
77 public void setEstoque( BigDecimal estoque ) {
78 this.estoque = estoque;
79 }
80
81 public Fornecedor getFornecedor() {
82 return fornecedor;
83 }
84
85 public void setFornecedor( Fornecedor fornecedor ) {
86 this.fornecedor = fornecedor;
87 }
88
89 public UnidadeMedida getUnidadeMedida() {
90 return unidadeMedida;
91 }
92
93 public void setUnidadeMedida( UnidadeMedida unidadeMedida ) {
94 this.unidadeMedida = unidadeMedida;
95 }
96
97 }
duas para escapá-la. O atributo message de @Pattern é usado para criarmos uma
mensagem personalizada para quando o produto for validado e esse atributo estiver
fora do que é esperado.
A anotação @PositiveOrZero indica que o atributo do tipo BigDecimal deve ser
zero ou qualquer valor positivo. As outras anotações que usaremos nas outras enti-
dades são @Positive para valores positivos obrigatoriamente (zero não é positivo
nem negativo!) e @Email para verificar se o valor representa um formato válido de
e-mail. A implementação de referência da Jakarta Bean Validation faz parte do Jakarta
EE 10, que estamos usando, e é fornecida pelo Hibernate Validator.
| Saiba Mais
O site oficial da especificação e da implementação da API Bean Validation pode
ser acessada pelo link <https://beanvalidation.org/>
A validação dos objetos será feita sempre nos Servlets e é implementada pelo mé-
todo validar , que tem sua implementação iniciada na linha 187 da classe Utils,
mostrada na Listagem 8.2.
1 package vendaprodutos.utils;
2
3 import java.math.BigDecimal;
4 import java.sql.Date;
5 import java.sql.PreparedStatement;
6 import java.sql.ResultSet;
7 import java.sql.SQLException;
8 import java.time.LocalDate;
9 import java.time.format.DateTimeFormatter;
10 import java.time.format.DateTimeParseException;
11 import java.util.Arrays;
12 import java.util.LinkedHashSet;
13 import java.util.List;
14 import java.util.Set;
15 import jakarta.servlet.RequestDispatcher;
16 import jakarta.servlet.http.HttpServletRequest;
17 import jakarta.validation.ConstraintViolation;
18 import jakarta.validation.Validation;
19 import jakarta.validation.Validator;
248 CAPÍTULO 8. SISTEMA PARA VENDA DE PRODUTOS
20
21 /**
22 * Classe de métodos utilitários.
23 *
24 * @author Prof. Dr. David Buzatto
25 */
26 public abstract class Utils {
27
28 // tanto o validador quanto o formatador
29 // são thread safe
30 // a implementação do validador normalmente tem cache
31 private static Validator validador = Validation
32 .buildDefaultValidatorFactory()
33 .getValidator();
34
35 private static DateTimeFormatter dtf = DateTimeFormatter
36 .ofPattern( "yyyy-MM-dd" );
37
38 /*
39 * Lê um parâmetro Long do request.
40 * Se a String for inválida, retorna null.
41 */
42 public static Long getLong(
43 HttpServletRequest request,
44 String nomeParametro ) {
45
46 Long v = null;
47
48 try {
49 v = Long.valueOf( request.getParameter( nomeParametro ) );
50 } catch ( NumberFormatException exc ) {
51 }
52
53 return v;
54
55 }
56
57 /*
58 * Lê um parâmetro BigDecimal do request.
59 * Se a String for inválida, retorna null.
60 */
61 public static BigDecimal getBigDecimal(
8.2. CONSTRUINDO O SISTEMA 249
62 HttpServletRequest request,
63 String nomeParametro ) {
64
65 BigDecimal v = null;
66
67 try {
68 v = new BigDecimal( request.getParameter( nomeParametro ) );
69 } catch ( NumberFormatException exc ) {
70 }
71
72 return v;
73
74 }
75
76 /*
77 * Converte uma String para Long.
78 * Se a String for inválida, retorna null.
79 */
80 public static Long getLong( String valor ) {
81
82 Long v = null;
83
84 try {
85 v = Long.valueOf( valor );
86 } catch ( NumberFormatException exc ) {
87 }
88
89 return v;
90
91 }
92
93 /*
94 * Converte uma String para BigDecimal.
95 * Se a String for inválida, retorna null.
96 */
97 public static BigDecimal getBigDecimal( String valor ) {
98
99 BigDecimal v = null;
100
101 try {
102 v = new BigDecimal( valor );
103 } catch ( NumberFormatException exc ) {
250 CAPÍTULO 8. SISTEMA PARA VENDA DE PRODUTOS
104 }
105
106 return v;
107
108 }
109
110 /*
111 * Converte uma String no formato dd/MM/yyyy
112 * para um java.sql.Date.
113 *
114 * Se a String for inválida, retorna null.
115 */
116 public static Date getDate( String data ) {
117
118 Date d = null;
119
120 try {
121 d = Date.valueOf( LocalDate.parse( data, dtf ) );
122 } catch ( DateTimeParseException exc ) {
123 }
124
125 return d;
126
127 }
128
129 /*
130 * Faz a leitura da chave primária após inserção no banco.
131 * Assume que o PreparedStatement foi configurado apropriadamente.
132 */
133 public static Long getChavePrimariaAposInsercao(
134 PreparedStatement stmt, String nomeColunaChave )
135 throws SQLException {
136
137 Long pk = null;
138
139 try ( ResultSet rsPK = stmt.getGeneratedKeys() ) {
140 if ( rsPK.next() ) {
141 pk = rsPK.getLong( nomeColunaChave );
142 }
143 }
144
145 return pk;
8.2. CONSTRUINDO O SISTEMA 251
146
147 }
148
149 /*
150 * Realiza a validação e retorna um conjunto de violações de
151 * restrições.
152 *
153 * Não insere no retorno os campos que devem ser ignorados.
154 */
155 private static Set<ConstraintViolation> validarObj(
156 Object obj,
157 String... ignorar ) {
158
159 // uma lista dos campos à ignorar
160 List<String> ignorarCampos = Arrays.<String>asList( ignorar );
161
162 // conjunto que conterá todas as violações de restrição
163 // que tenham caminho da propriedade igual
164 // à alguma da lista de ignorar
165 Set<ConstraintViolation> cvs = new LinkedHashSet<>();
166
167 // valida e percorre todas as restrições violadas
168 for ( ConstraintViolation cv : validador.validate( obj ) ) {
169
170 // se a lista de campos à ignorar não tiver
171 // o caminho da propriedade
172 if ( !ignorarCampos.contains(
173 cv.getPropertyPath().toString() ) ) {
174 cvs.add( cv );
175 }
176 }
177
178 return cvs;
179
180 }
181
182 /*
183 * Realiza a validação do objeto passado e lança
184 * uma SQLException com todos os erros obtidos caso o
185 * objeto seja inválido.
186 */
187 public static void validar(
252 CAPÍTULO 8. SISTEMA PARA VENDA DE PRODUTOS
230 }
8.2.2 DAO
Temos duas novidades na nossa camada de persistência. A primeira é a implementa-
ção da interface AutoCloseable que permitirá que usemos objetos dos nossos DAOs
na construção try-with-resources que, por sua vez, fará o fechamento automático
da conexão dos DAOs ao terminarem de serem utilizados. Para isso precisaremos
implementar o método close que substituirá o nosso antigo fecharConexao . A
implementação do DAO genérico é mostrada na Listagem 8.3, sendo que o método
close pode ser visto entre as linhas 57 e 59.
1 package vendaprodutos.dao;
2
3 import java.sql.Connection;
4 import java.sql.SQLException;
5 import java.util.List;
6 import vendaprodutos.jdbc.ConnectionFactory;
7
8 /**
9 * DAO genérico que implementa a interface AutoCloseable,
10 * permitindo o uso da construção try-with-resources.
11 *
12 * @author Prof. Dr. David Buzatto
254 CAPÍTULO 8. SISTEMA PARA VENDA DE PRODUTOS
13 */
14 public abstract class DAO<Tipo> implements AutoCloseable {
15
16 // cada DAO terá uma conexão.
17 private Connection conexao;
18
19 /**
20 * Construtor do DAO.
21 * É nesse construtor que a conexão é criada.
22 *
23 * @throws SQLException
24 */
25 public DAO() throws SQLException {
26
27 /*
28 * Usa-se o método getConnection() da fábrica de conexões,
29 * para criar uma conexão para o DAO.
30 */
31 conexao = ConnectionFactory.getConnection();
32
33 }
34
35 /**
36 * Método para obter a conexão criada.
37 *
38 * @return Retorna a conexão.
39 */
40 public Connection getConnection() {
41 return conexao;
42 }
43
44 /**
45 * Método para fechar a conexão aberta.
46 *
47 * Não precisa ser invocado explicitamente caso
48 * o objeto do DAO tenha sido criado usando a construção
49 * try-with-resources.
50 *
51 * É sobrescrito da interface AutoCloseable.
52 *
53 * @throws SQLException Caso ocorra algum erro
54 * durante o fechamento da conexão.
8.2. CONSTRUINDO O SISTEMA 255
55 */
56 @Override
57 public void close() throws SQLException {
58 conexao.close();
59 }
60
61 /**
62 * Método abstrato para salvar uma instância de uma
63 * entidade da base de dados.
64 *
65 * É o "C" do CRUD.
66 *
67 * @param obj Instância do objeto da entidade a ser salvo.
68 * @throws SQLException Caso ocorra algum erro durante a gravação.
69 */
70 public abstract void salvar( Tipo obj ) throws SQLException;
71
72 /**
73 * Método abstrato para atualizar uma instância de uma
74 * entidade da base de dados.
75 *
76 * É o "U" do CRUD.
77 *
78 * @param obj Instância do objeto da entidade a ser atualizado.
79 * @throws SQLException Caso ocorra algum erro durante a atualização.
80 */
81 public abstract void atualizar( Tipo obj ) throws SQLException;
82
83 /**
84 * Método abstrato para excluir uma instância de uma
85 * entidade da base de dados.
86 *
87 * É o "D" do CRUD.
88 *
89 * @param obj Instância do objeto da entidade a ser salvo.
90 * @throws SQLException Caso ocorra algum erro durante a exclusão.
91 */
92 public abstract void excluir( Tipo obj ) throws SQLException;
93
94 /**
95 * Método abstrato para obter todas as instâncias de uma
96 * entidade da base de dados.
256 CAPÍTULO 8. SISTEMA PARA VENDA DE PRODUTOS
97 *
98 * É o "R" do CRUD.
99 *
100 * @return Lista de todas as instâncias da entidade.
101 * @throws SQLException Caso ocorra algum erro durante a consulta.
102 */
103 public abstract List<Tipo> listarTodos() throws SQLException;
104
105 /**
106 * Método abstrato para obter uma instância de uma
107 * entidade pesquisando pelo seu atributo identificador.
108 *
109 * É o "R" do CRUD.
110 *
111 * @param id Identificador da instância a ser obtida.
112 * @return Instância relacionada ao id passado, ou null caso não seja
113 * encontrada.
114 * @throws SQLException Caso ocorra algum erro durante a consulta.
115 */
116 public abstract Tipo obterPorId( Long id ) throws SQLException;
117
118 }
A outra mudança que temos em nossos DAOs é que agora, toda entidade que ti-
ver um objeto submetido ao método salvar(...) , será modificada para conter o
identificador que foi gerado pelo SGBD. Na Listagem 8.4 é apresentado o DAO para
produtos.
1 package vendaprodutos.dao;
2
3 import java.sql.PreparedStatement;
4 import java.sql.ResultSet;
5 import java.sql.SQLException;
6 import java.util.ArrayList;
7 import java.util.List;
8 import vendaprodutos.entidades.Cidade;
9 import vendaprodutos.entidades.Estado;
10 import vendaprodutos.entidades.Fornecedor;
8.2. CONSTRUINDO O SISTEMA 257
11 import vendaprodutos.entidades.Produto;
12 import vendaprodutos.entidades.UnidadeMedida;
13 import vendaprodutos.utils.Utils;
14
15 /**
16 * DAO para a entidade Produto.
17 *
18 * @author Prof. Dr. David Buzatto
19 */
20 public class ProdutoDAO extends DAO<Produto> {
21
22 public ProdutoDAO() throws SQLException {
23 }
24
25 @Override
26 public void salvar( Produto obj ) throws SQLException {
27
28 PreparedStatement stmt = getConnection().prepareStatement(
29 "INSERT INTO " +
30 "produto(" +
31 " descricao, " +
32 " codigoBarras, " +
33 " valorVenda, " +
34 " estoque, " +
35 " fornecedor_id, " +
36 " unidade_medida_id ) " +
37 "VALUES( ?, ?, ?, ?, ?, ? );",
38 new String[]{ "insert_id" } ); // para retorno da chave
39 // primária gerada
40
41 stmt.setString( 1, obj.getDescricao() );
42 stmt.setString( 2, obj.getCodigoBarras() );
43 stmt.setBigDecimal( 3, obj.getValorVenda() );
44 stmt.setBigDecimal( 4, obj.getEstoque() );
45 stmt.setLong( 5, obj.getFornecedor().getId() );
46 stmt.setLong( 6, obj.getUnidadeMedida().getId() );
47
48 stmt.executeUpdate();
49 obj.setId( Utils.getChavePrimariaAposInsercao( stmt, "insert_id" ) );
50 stmt.close();
51
52 }
258 CAPÍTULO 8. SISTEMA PARA VENDA DE PRODUTOS
53
54 @Override
55 public void atualizar( Produto obj ) throws SQLException {
56
57 PreparedStatement stmt = getConnection().prepareStatement(
58 "UPDATE produto " +
59 "SET" +
60 " descricao = ?, " +
61 " codigoBarras = ?," +
62 " valorVenda = ?, " +
63 " estoque = ?, " +
64 " fornecedor_id = ?, " +
65 " unidade_medida_id = ? " +
66 "WHERE" +
67 " id = ?;" );
68
69 stmt.setString( 1, obj.getDescricao() );
70 stmt.setString( 2, obj.getCodigoBarras() );
71 stmt.setBigDecimal( 3, obj.getValorVenda() );
72 stmt.setBigDecimal( 4, obj.getEstoque() );
73 stmt.setLong( 5, obj.getFornecedor().getId() );
74 stmt.setLong( 6, obj.getUnidadeMedida().getId() );
75 stmt.setLong( 7, obj.getId() );
76
77 stmt.executeUpdate();
78 stmt.close();
79
80 }
81
82 @Override
83 public void excluir( Produto obj ) throws SQLException {
84
85 PreparedStatement stmt = getConnection().prepareStatement(
86 "DELETE FROM produto " +
87 "WHERE" +
88 " id = ?;" );
89
90 stmt.setLong( 1, obj.getId() );
91
92 stmt.executeUpdate();
93 stmt.close();
94
8.2. CONSTRUINDO O SISTEMA 259
95 }
96
97 @Override
98 public List<Produto> listarTodos() throws SQLException {
99
100 List<Produto> lista = new ArrayList<>();
101
102 PreparedStatement stmt = getConnection().prepareStatement(
103 "SELECT" +
104 " p.id idProduto, " +
105 " p.descricao descricaoProduto, " +
106 " p.codigoBarras codigoBarrasProduto, " +
107 " p.valorVenda valorVendaProduto, " +
108 " p.estoque estoqueProduto, " +
109 " u.id idUnidadeMedida, " +
110 " u.descricao descricaoUnidadeMedida, " +
111 " u.sigla siglaUnidadeMedida, " +
112 " f.id idFornecedor, " +
113 " f.razaoSocial razaoSocialFornecedor, " +
114 " f.cnpj cnpjFornecedor, " +
115 " f.email emailFornecedor, " +
116 " f.logradouro logradouroFornecedor, " +
117 " f.numero numeroFornecedor, " +
118 " f.bairro bairroFornecedor, " +
119 " f.cep cepFornecedor, " +
120 " ci.id idCidade, " +
121 " ci.nome nomeCidade, " +
122 " e.id idEstado, " +
123 " e.nome nomeEstado, " +
124 " e.sigla siglaEstado " +
125 "FROM" +
126 " produto p, " +
127 " unidade_medida u, " +
128 " fornecedor f, " +
129 " cidade ci, " +
130 " estado e " +
131 "WHERE" +
132 " p.unidade_medida_id = u.id AND " +
133 " p.fornecedor_id = f.id AND " +
134 " f.cidade_id = ci.id AND " +
135 " ci.estado_id = e.id " +
136 "ORDER BY p.descricao;" );
260 CAPÍTULO 8. SISTEMA PARA VENDA DE PRODUTOS
137
138 ResultSet rs = stmt.executeQuery();
139
140 while ( rs.next() ) {
141
142 Produto p = new Produto();
143 UnidadeMedida u = new UnidadeMedida();
144 Fornecedor f = new Fornecedor();
145 Cidade ci = new Cidade();
146 Estado e = new Estado();
147
148 p.setId( rs.getLong( "idProduto" ) );
149 p.setDescricao( rs.getString( "descricaoProduto" ) );
150 p.setCodigoBarras( rs.getString( "codigoBarrasProduto" ) );
151 p.setValorVenda( rs.getBigDecimal( "valorVendaProduto" ) );
152 p.setEstoque( rs.getBigDecimal( "estoqueProduto" ) );
153 p.setUnidadeMedida( u );
154 p.setFornecedor( f );
155
156 u.setId( rs.getLong( "idUnidadeMedida" ) );
157 u.setDescricao( rs.getString( "descricaoUnidadeMedida" ) );
158 u.setSigla( rs.getString( "siglaUnidadeMedida" ) );
159
160 f.setId( rs.getLong( "idFornecedor" ) );
161 f.setRazaoSocial( rs.getString( "razaoSocialFornecedor" ) );
162 f.setCnpj( rs.getString( "cnpjFornecedor" ) );
163 f.setEmail( rs.getString( "emailFornecedor" ) );
164 f.setLogradouro( rs.getString( "logradouroFornecedor" ) );
165 f.setNumero( rs.getString( "numeroFornecedor" ) );
166 f.setBairro( rs.getString( "bairroFornecedor" ) );
167 f.setCep( rs.getString( "cepFornecedor" ) );
168 f.setCidade( ci );
169
170 ci.setId( rs.getLong( "idCidade" ) );
171 ci.setNome( rs.getString( "nomeCidade" ) );
172 ci.setEstado( e );
173
174 e.setId( rs.getLong( "idEstado" ) );
175 e.setNome( rs.getString( "nomeEstado" ) );
176 e.setSigla( rs.getString( "siglaEstado" ) );
177
178 lista.add( p );
8.2. CONSTRUINDO O SISTEMA 261
179
180 }
181
182 rs.close();
183 stmt.close();
184
185 return lista;
186
187 }
188
189 @Override
190 public Produto obterPorId( Long id ) throws SQLException {
191
192 Produto produto = null;
193
194 PreparedStatement stmt = getConnection().prepareStatement(
195 "SELECT" +
196 " p.id idProduto, " +
197 " p.descricao descricaoProduto, " +
198 " p.codigoBarras codigoBarrasProduto, " +
199 " p.valorVenda valorVendaProduto, " +
200 " p.estoque estoqueProduto, " +
201 " u.id idUnidadeMedida, " +
202 " u.descricao descricaoUnidadeMedida, " +
203 " u.sigla siglaUnidadeMedida, " +
204 " f.id idFornecedor, " +
205 " f.razaoSocial razaoSocialFornecedor, " +
206 " f.cnpj cnpjFornecedor, " +
207 " f.email emailFornecedor, " +
208 " f.logradouro logradouroFornecedor, " +
209 " f.numero numeroFornecedor, " +
210 " f.bairro bairroFornecedor, " +
211 " f.cep cepFornecedor, " +
212 " ci.id idCidade, " +
213 " ci.nome nomeCidade, " +
214 " e.id idEstado, " +
215 " e.nome nomeEstado, " +
216 " e.sigla siglaEstado " +
217 "FROM" +
218 " produto p, " +
219 " unidade_medida u, " +
220 " fornecedor f, " +
262 CAPÍTULO 8. SISTEMA PARA VENDA DE PRODUTOS
263
264 ci.setId( rs.getLong( "idCidade" ) );
265 ci.setNome( rs.getString( "nomeCidade" ) );
266 ci.setEstado( e );
267
268 e.setId( rs.getLong( "idEstado" ) );
269 e.setNome( rs.getString( "nomeEstado" ) );
270 e.setSigla( rs.getString( "siglaEstado" ) );
271
272 }
273
274 rs.close();
275 stmt.close();
276
277 return produto;
278
279 }
280
281 /**
282 * Atualização do estoque para o cancelamento de vendas.
283 */
284 public void atualizarEstoque( Produto obj ) throws SQLException {
285
286 PreparedStatement stmt = getConnection().prepareStatement(
287 "UPDATE produto " +
288 "SET" +
289 " estoque = ? " +
290 "WHERE" +
291 " id = ?;" );
292
293 stmt.setBigDecimal( 1, obj.getEstoque() );
294 stmt.setLong( 2, obj.getId() );
295
296 stmt.executeUpdate();
297 stmt.close();
298
299 }
300
301 }
String contendo o código SQL que será executado, é passado um array de Strings
com o nome da ou das colunas que representam as chaves primárias daquela ta-
bela. Essa “artimanha” funciona para colunas que são auto-incrementáveis, que é o
caso dos nossos identificadores. Precisaremos dessa característica no cadastro das
vendas. Veremos isso mais para frente. Quando fazemos uso desse recurso, após a
persistência do objeto, que gerará uma nova linha/registro/tupla na tabela, precisa-
remos usar o mesmo PreparedStatement para obter a ou as chaves. Isso será feito
no método getChavePrimariaAposInsercao(...) da classe Utils (linha 133 da
Listagem 8.2). Como consistentemente estamos usando uma coluna chamada id
como chave primária auto-incrementável, usaremos esse método para todas as nos-
sas entidades, com exceção da ItemVenda que funcionará de outra forma. Note que o
nome que foi usado é insert_id, requisito necessário da versão atual do driver JDBC
que estamos utilizando.
Muito bem, temos nossas entidades prontas para serem validadas e a camada de
persistência atualizada. Vamos ver agora o que mudou nos nossos Servlets.
8.2.3 Servlets
Nossos Servlets continuarão a funcionar e a ter a mesma estrutura que vimos anteri-
ormente, mas agora com algumas melhorias. Na Listagem 8.5 pode ser visto o código
completo do Servlet de produtos.
1 package vendaprodutos.controladores;
2
3 import java.io.IOException;
4 import java.math.BigDecimal;
5 import java.sql.SQLException;
6 import jakarta.servlet.RequestDispatcher;
7 import jakarta.servlet.ServletException;
8 import jakarta.servlet.annotation.WebServlet;
9 import jakarta.servlet.http.HttpServlet;
10 import jakarta.servlet.http.HttpServletRequest;
11 import jakarta.servlet.http.HttpServletResponse;
12 import vendaprodutos.dao.FornecedorDAO;
13 import vendaprodutos.dao.ProdutoDAO;
14 import vendaprodutos.dao.UnidadeMedidaDAO;
15 import vendaprodutos.entidades.Fornecedor;
8.2. CONSTRUINDO O SISTEMA 265
16 import vendaprodutos.entidades.Produto;
17 import vendaprodutos.entidades.UnidadeMedida;
18 import vendaprodutos.utils.Utils;
19
20 /**
21 * Servlet para tratar Produtos.
22 *
23 * @author Prof. Dr. David Buzatto
24 */
25 @WebServlet( name = "ProdutosServlet",
26 urlPatterns = { "/processaProdutos" } )
27 public class ProdutosServlet extends HttpServlet {
28
29 protected void processRequest(
30 HttpServletRequest request,
31 HttpServletResponse response )
32 throws ServletException, IOException {
33
34 String acao = request.getParameter( "acao" );
35 RequestDispatcher disp = null;
36
37 try ( ProdutoDAO daoProduto = new ProdutoDAO();
38 FornecedorDAO daoFornecedor = new FornecedorDAO();
39 UnidadeMedidaDAO daoUnidadeMedida = new UnidadeMedidaDAO() ) {
40
41 if ( acao.equals( "inserir" ) ) {
42
43 String descricao = request.getParameter( "descricao" );
44 String codigoBarras = request.getParameter( "codigoBarras" );
45 BigDecimal valorVenda = Utils.getBigDecimal(
46 request, "valorVenda" );
47 BigDecimal estoque = Utils.getBigDecimal(
48 request, "estoque" );
49 Long idFornecedor = Utils.getLong(
50 request, "idFornecedor" );
51 Long idUnidadeMedida = Utils.getLong(
52 request, "idUnidadeMedida" );
53
54 Fornecedor f = daoFornecedor.obterPorId( idFornecedor );
55 UnidadeMedida u = daoUnidadeMedida.obterPorId( idUnidadeMedida
,→ );
56
266 CAPÍTULO 8. SISTEMA PARA VENDA DE PRODUTOS
98 "/formularios/produtos/listagem.jsp" );
99
100 } else if ( acao.equals( "excluir" ) ) {
101
102 Long id = Utils.getLong( request, "id" );
103 Produto p = daoProduto.obterPorId( id );
104
105 daoProduto.excluir( p );
106 disp = request.getRequestDispatcher(
107 "/formularios/produtos/listagem.jsp" );
108
109 } else {
110
111 Long id = Utils.getLong( request, "id" );
112 Produto p = daoProduto.obterPorId( id );
113 request.setAttribute( "produto", p );
114
115 if ( acao.equals( "prepararAlteracao" ) ) {
116 disp = request.getRequestDispatcher(
117 "/formularios/produtos/alterar.jsp" );
118 } else if ( acao.equals( "prepararExclusao" ) ) {
119 disp = request.getRequestDispatcher(
120 "/formularios/produtos/excluir.jsp" );
121 }
122
123 }
124
125 } catch ( SQLException exc ) {
126 disp = Utils.prepararDespachoErro( request, exc.getMessage() );
127 }
128
129 if ( disp != null ) {
130 disp.forward( request, response );
131 }
132
133 }
134
135 @Override
136 protected void doGet(
137 HttpServletRequest request,
138 HttpServletResponse response )
139 throws ServletException, IOException {
268 CAPÍTULO 8. SISTEMA PARA VENDA DE PRODUTOS
23 </ul>
24 </div>
25
26 <a href="${requestScope.voltarPara}">Voltar</a>
27
28 </body>
29
30 </html>
Com o conhecimento que você já adquiriu com o que estamos trabalhando você será
capaz de entender o que essa página faz.
Back-End
Na Listagem 8.7 pode ser vista a entidade Venda. Uma venda só poderá ser cadastrada
e, caso necessário, ser cancelada. Estamos tentando simular um sistema real com
anotações fiscais e esse tipo de coisa precisa acontecer, ou seja, uma venda nunca
deve ser excluída! Para o tratamento do cancelamento temos o atributo cancelada.
1 package vendaprodutos.entidades;
2
3 import java.sql.Date;
4 import jakarta.validation.constraints.NotNull;
5
6 /**
7 * Entidade Venda.
8 *
9 * @author Prof. Dr. David Buzatto
10 */
11 public class Venda {
12
13 @NotNull
8.2. CONSTRUINDO O SISTEMA 271
56
57 }
1 package vendaprodutos.entidades;
2
3 import java.math.BigDecimal;
4 import jakarta.validation.constraints.NotNull;
5 import jakarta.validation.constraints.Positive;
6 import jakarta.validation.constraints.PositiveOrZero;
7
8 /**
9 * Entidade ItemVenda.
10 *
11 * @author Prof. Dr. David Buzatto
12 */
13 public class ItemVenda {
14
15 @NotNull
16 private Venda venda;
17
18 @NotNull
19 private Produto produto;
20
21 @NotNull
22 @PositiveOrZero
23 private BigDecimal valor;
24
25 @NotNull
26 @Positive
27 private BigDecimal quantidade;
28
29 public Venda getVenda() {
30 return venda;
31 }
32
33 public void setVenda( Venda venda ) {
8.2. CONSTRUINDO O SISTEMA 273
34 this.venda = venda;
35 }
36
37 public Produto getProduto() {
38 return produto;
39 }
40
41 public void setProduto( Produto produto ) {
42 this.produto = produto;
43 }
44
45 public BigDecimal getValor() {
46 return valor;
47 }
48
49 public void setValor( BigDecimal valor ) {
50 this.valor = valor;
51 }
52
53 public BigDecimal getQuantidade() {
54 return quantidade;
55 }
56
57 public void setQuantidade( BigDecimal quantidade ) {
58 this.quantidade = quantidade;
59 }
60
61 }
Cada venda pode conter um ou mais produtos que, por sua vez, podem ter sido vendi-
dos em uma ou mais vendas, sendo assim, precisamos ter uma entidade que associa
as outras duas. O valor do item da venda é o valor do produto naquele momento em
que foi vendido, visto que o valor de venda de um produto pode variar com o tempo,
mas o valor usado no momento da venda deve ser mantido! Além disso, todo item
da venda tem uma quantidade associada, pois podemos ter comprado, por exemplo,
duas caixas de ovos ou um quilo e meio de peito de frango.
O código do DAO que trata as vendas é apresentado na Listagem 8.9. O método
atualizar(...) será usado para cancelar uma venda. Além disso, não há razão
para excluirmos vendas realizadas, sendo assim, o corpo do método está vazio.
274 CAPÍTULO 8. SISTEMA PARA VENDA DE PRODUTOS
1 package vendaprodutos.dao;
2
3 import java.sql.PreparedStatement;
4 import java.sql.ResultSet;
5 import java.sql.SQLException;
6 import java.util.ArrayList;
7 import java.util.List;
8 import vendaprodutos.entidades.Cidade;
9 import vendaprodutos.entidades.Cliente;
10 import vendaprodutos.entidades.Estado;
11 import vendaprodutos.entidades.Venda;
12 import vendaprodutos.utils.Utils;
13
14 /**
15 * DAO para a entidade Venda.
16 *
17 * @author Prof. Dr. David Buzatto
18 */
19 public class VendaDAO extends DAO<Venda> {
20
21 public VendaDAO() throws SQLException {
22 }
23
24 @Override
25 public void salvar( Venda obj ) throws SQLException {
26
27 PreparedStatement stmt = getConnection().prepareStatement(
28 "INSERT INTO " +
29 "venda(" +
30 " data, " +
31 " cancelada, " +
32 " cliente_id ) " +
33 "VALUES( ?, ?, ? );",
34 new String[]{ "insert_id" } );
35
36 stmt.setDate( 1, obj.getData() );
37 stmt.setBoolean( 2, obj.getCancelada() );
38 stmt.setLong( 3, obj.getCliente().getId() );
39
8.2. CONSTRUINDO O SISTEMA 275
40 stmt.executeUpdate();
41 obj.setId( Utils.getChavePrimariaAposInsercao( stmt, "insert_id" ) );
42 stmt.close();
43
44 }
45
46 @Override
47 public void atualizar( Venda obj ) throws SQLException {
48
49 // a atualização das vendas será a de cancelamento
50
51 PreparedStatement stmt = getConnection().prepareStatement(
52 "UPDATE venda " +
53 "SET" +
54 " data = ?, " +
55 " cancelada = ?, " +
56 " cliente_id = ? " +
57 "WHERE" +
58 " id = ?;" );
59
60 stmt.setDate( 1, obj.getData() );
61 stmt.setBoolean( 2, obj.getCancelada() );
62 stmt.setLong( 3, obj.getCliente().getId() );
63 stmt.setLong( 4, obj.getId() );
64
65 stmt.executeUpdate();
66 stmt.close();
67
68 }
69
70 @Override
71 public void excluir( Venda obj ) throws SQLException {
72 // vendas não são excluídas!
73 }
74
75 @Override
76 public List<Venda> listarTodos() throws SQLException {
77
78 List<Venda> lista = new ArrayList<>();
79
80 PreparedStatement stmt = getConnection().prepareStatement(
81 "SELECT" +
276 CAPÍTULO 8. SISTEMA PARA VENDA DE PRODUTOS
124
125 c.setId( rs.getLong( "idCliente" ) );
126 c.setNome( rs.getString( "nomeCliente" ) );
127 c.setSobrenome( rs.getString( "sobrenomeCliente" ) );
128 c.setDataNascimento( rs.getDate( "dataNascimentoCliente" ) );
129 c.setCpf( rs.getString( "cpfCliente" ) );
130 c.setEmail( rs.getString( "emailCliente" ) );
131 c.setLogradouro( rs.getString( "logradouroCliente" ) );
132 c.setNumero( rs.getString( "numeroCliente" ) );
133 c.setBairro( rs.getString( "bairroCliente" ) );
134 c.setCep( rs.getString( "cepCliente" ) );
135 c.setCidade( ci );
136
137 ci.setId( rs.getLong( "idCidade" ) );
138 ci.setNome( rs.getString( "nomeCidade" ) );
139 ci.setEstado( e );
140
141 e.setId( rs.getLong( "idEstado" ) );
142 e.setNome( rs.getString( "nomeEstado" ) );
143 e.setSigla( rs.getString( "siglaEstado" ) );
144
145 lista.add( v );
146
147 }
148
149 rs.close();
150 stmt.close();
151
152 return lista;
153
154 }
155
156 @Override
157 public Venda obterPorId( Long id ) throws SQLException {
158
159 Venda venda = null;
160
161 PreparedStatement stmt = getConnection().prepareStatement(
162 "SELECT" +
163 " v.id idVenda, " +
164 " v.data dataVenda, " +
165 " v.cancelada vendaCancelada, " +
278 CAPÍTULO 8. SISTEMA PARA VENDA DE PRODUTOS
1 package vendaprodutos.dao;
2
3 import java.sql.PreparedStatement;
4 import java.sql.ResultSet;
5 import java.sql.SQLException;
280 CAPÍTULO 8. SISTEMA PARA VENDA DE PRODUTOS
6 import java.util.ArrayList;
7 import java.util.List;
8 import vendaprodutos.entidades.ItemVenda;
9 import vendaprodutos.entidades.Produto;
10
11 /**
12 * DAO para a entidade ItemVenda.
13 *
14 * @author Prof. Dr. David Buzatto
15 */
16 public class ItemVendaDAO extends DAO<ItemVenda> {
17
18 public ItemVendaDAO() throws SQLException {
19 }
20
21 @Override
22 public void salvar( ItemVenda obj ) throws SQLException {
23
24 PreparedStatement stmt = getConnection().prepareStatement(
25 "INSERT INTO " +
26 "item_venda( venda_id, produto_id, valor, quantidade ) " +
27 "VALUES( ?, ?, ?, ? );" );
28
29 stmt.setLong( 1, obj.getVenda().getId() );
30 stmt.setLong( 2, obj.getProduto().getId() );
31 stmt.setBigDecimal( 3, obj.getValor() );
32 stmt.setBigDecimal( 4, obj.getQuantidade() );
33
34 stmt.executeUpdate();
35 stmt.close();
36
37 }
38
39 @Override
40 public void atualizar( ItemVenda obj ) throws SQLException {
41 // não faz sentido na nossa implementação,
42 // pois não é possível atualizar um item
43 // da venda armazenado
44 }
45
46 @Override
47 public void excluir( ItemVenda obj ) throws SQLException {
8.2. CONSTRUINDO O SISTEMA 281
89 "FROM" +
90 " item_venda iv, " +
91 " produto p " +
92 "WHERE iv.produto_id = p.id AND " +
93 " iv.venda_id = ?;" );
94
95 stmt.setLong( 1, idVenda );
96
97 ResultSet rs = stmt.executeQuery();
98
99 while ( rs.next() ) {
100
101 ItemVenda iv = new ItemVenda();
102 Produto p = new Produto();
103
104 iv.setQuantidade( rs.getBigDecimal( "quantidadeItemVenda" ) );
105 iv.setProduto( p );
106
107 p.setId( rs.getLong( "idProduto" ) );
108 p.setEstoque( rs.getBigDecimal( "estoqueProduto" ) );
109
110 itensVenda.add( iv );
111
112 }
113
114 rs.close();
115 stmt.close();
116
117 return itensVenda;
118
119 }
120
121 }
1 package vendaprodutos.controladores;
2
3 import java.io.IOException;
4 import java.io.PrintWriter;
5 import java.io.StringReader;
6 import java.math.BigDecimal;
7 import java.sql.Date;
8 import java.sql.SQLException;
9 import java.time.LocalDate;
10 import jakarta.json.Json;
11 import jakarta.json.JsonArray;
12 import jakarta.json.JsonObject;
13 import jakarta.json.JsonReader;
14 import jakarta.json.JsonValue;
15 import jakarta.servlet.RequestDispatcher;
16 import jakarta.servlet.ServletException;
17 import jakarta.servlet.annotation.WebServlet;
18 import jakarta.servlet.http.HttpServlet;
19 import jakarta.servlet.http.HttpServletRequest;
20 import jakarta.servlet.http.HttpServletResponse;
21 import vendaprodutos.dao.ClienteDAO;
22 import vendaprodutos.dao.ItemVendaDAO;
23 import vendaprodutos.dao.ProdutoDAO;
24 import vendaprodutos.dao.VendaDAO;
25 import vendaprodutos.entidades.Cliente;
26 import vendaprodutos.entidades.ItemVenda;
27 import vendaprodutos.entidades.Produto;
28 import vendaprodutos.entidades.Venda;
29 import vendaprodutos.utils.Utils;
30
31 /**
32 * Servlet para tratar Vendas.
33 *
34 * @author Prof. Dr. David Buzatto
284 CAPÍTULO 8. SISTEMA PARA VENDA DE PRODUTOS
35 */
36 @WebServlet( name = "VendasServlet",
37 urlPatterns = { "/processaVendas" } )
38 public class VendasServlet extends HttpServlet {
39
40 protected void processRequest(
41 HttpServletRequest request,
42 HttpServletResponse response )
43 throws ServletException, IOException {
44
45 String acao = request.getParameter( "acao" );
46 RequestDispatcher disp = null;
47
48 try ( VendaDAO daoVenda = new VendaDAO();
49 ClienteDAO daoCliente = new ClienteDAO();
50 ItemVendaDAO daoItemVenda = new ItemVendaDAO();
51 ProdutoDAO daoProduto = new ProdutoDAO() ) {
52
53 if ( acao.equals( "inserir" ) ) {
54
55 Long idCliente = Utils.getLong( request, "idCliente" );
56 String itensVenda = request.getParameter( "itensVenda" );
57
58 // cria um leitor de json para processar os
59 // itens da venda
60 JsonReader jsr = Json.createReader(
61 new StringReader( itensVenda ) );
62 // faz a leitura/parse
63 JsonArray jsaItensVenda = jsr.readArray();
64
65 Cliente c = daoCliente.obterPorId( idCliente );
66
67 Venda v = new Venda();
68 v.setData( Date.valueOf( LocalDate.now() ) );
69 v.setCancelada( false );
70 v.setCliente( c );
71
72 Utils.validar( v, "id" );
73 daoVenda.salvar( v );
74
75 // itera pelos itens da venda genéricos
76 for ( JsonValue jsv : jsaItensVenda ) {
8.2. CONSTRUINDO O SISTEMA 285
77
78 // sabemos que cada item é um objeto
79 JsonObject jso = jsv.asJsonObject();
80
81 // extraímos os atributos
82 Long idProduto = Utils.getLong(
83 jso.getString( "idProduto" ) );
84 BigDecimal quantidade = Utils.getBigDecimal(
85 jso.getString( "quantidade" ) );
86
87 // obtém o produto e atualiza o estoque
88 Produto p = daoProduto.obterPorId( idProduto );
89 p.setEstoque( p.getEstoque().subtract( quantidade ) );
90
91 // cria um item da venda
92 ItemVenda iv = new ItemVenda();
93 iv.setVenda( v );
94 iv.setProduto( p );
95 iv.setValor( p.getValorVenda() );
96 iv.setQuantidade( quantidade );
97
98 // não validaremos o produto, pois
99 // permitiremos estoque negativo na venda
100 daoProduto.atualizar( p );
101 daoItemVenda.salvar( iv );
102
103 }
104
105 disp = request.getRequestDispatcher(
106 "/formularios/vendas/listagem.jsp" );
107
108 } else if ( acao.equals( "cancelar" ) ) {
109
110 Long id = Utils.getLong( request, "id" );
111
112 Venda v = daoVenda.obterPorId( id );
113 v.setCancelada( true );
114 daoVenda.atualizar( v );
115
116 for ( ItemVenda iv : daoItemVenda.obterPorIdVenda( id ) ) {
117 Produto p = iv.getProduto();
118 p.setEstoque( p.getEstoque().add( iv.getQuantidade() ) );
286 CAPÍTULO 8. SISTEMA PARA VENDA DE PRODUTOS
119 daoProduto.atualizarEstoque( p );
120 }
121
122 response.setContentType( "application/json;charset=UTF-8" );
123
124 JsonObject jo = Json.createObjectBuilder()
125 .add( "status", "ok" )
126 .build();
127
128 try ( PrintWriter out = response.getWriter() ) {
129 out.print( jo );
130 }
131
132 }
133
134 } catch ( SQLException exc ) {
135 disp = Utils.prepararDespachoErro( request, exc.getMessage() );
136 }
137
138 if ( disp != null ) {
139 disp.forward( request, response );
140 }
141
142 }
143
144 @Override
145 protected void doGet(
146 HttpServletRequest request,
147 HttpServletResponse response )
148 throws ServletException, IOException {
149 processRequest( request, response );
150 }
151
152 @Override
153 protected void doPost(
154 HttpServletRequest request,
155 HttpServletResponse response )
156 throws ServletException, IOException {
157 processRequest( request, response );
158 }
159
160 @Override
8.2. CONSTRUINDO O SISTEMA 287
Esse Servlet tratará dois tipos de ação. Uma de inserção, entre as linhas 55 e 106 e
uma de cancelamento, entre as linhas 110 e 130. A inserção de uma venda envolve
a associação do cliente que está comprando do estabelecimento, a data da mesma
e todos os itens que a compõe. Veja que a variável itensVenda é uma String que
conterá dados codificados em JSON que virão do cliente. Precisaremos processar
esse JSON do lado do servidor parar criar um objeto genérico que conterá o ou os
identificadores do produtos e a ou as respectivas quantidades vendidas. O processo
de construção desse JSON será visto quando formos tratar sobre o lado do cliente.
Veremos isso logo.
Veja que o processamento do JSON dos itens da venda é feito inicialmente criando um
objeto do tipo JsonReader nas linhas 60 e 61. Posteriormente, na linha 63 o processo
de parsing do JSON é feito, atribuindo o resultado à variável jsaItensVenda do tipo
JsonArray. Após salvarmos a venda na linha 73, iteraremos sobre jsaItensVenda
para extrairmos cada objeto genérico com os dados que precisamos para “amarrar”
os produtos vendidos na venda realizada. Essa iteração ocorre entre as linhas 76
e 103, onde inicialmente obtemos o objeto genérico atual (linha 79), extraímos os
dados necessários entre as linhas 82 e 85 e na linha 88 consultamos o produto e
atualizamos o seu estoque (só no objeto por enquanto), visto que como estamos
vendendo, precisamos subtrair a quantidade do estoque. Entre as linhas 92 e 96
criamos o item da venda, na linha 100 atualizamos o produto por causa do estoque
(agora no banco de dados) e na linha 101 salvamos o item da venda. Não precisamos
validar os itens de venda nem os produtos, visto que todo esse processamento será
feito internamente.
O processo de cancelamento, tratado entre as linhas 110 e 130, atualizará a venda,
marcando-a como cancelada e atualizará os estoques dos produtos associados. Note
que não criaremos um RequestDispatcher, pois a ação de cancelamento será execu-
tada via AJAX. Não trataremos situações em que possam haver problemas no SGBD,
pois após o cancelamento de uma venda retornaremos um JSON sinalizando que a
requisição foi bem sucedida, mas em um sistema mais robusto teríamos que pensar
nisso também. Uma outra coisa a se pensar, mas que não foi endereçada na nossa im-
plementação, seria o caso de haver algum erro durante a inserção dos itens de venda
ou na atualização dos produtos no cancelamento. Isso seria resolvido configurando o
driver do SGBD para que as transações fossem gerenciadas manualmente e iniciadas
288 CAPÍTULO 8. SISTEMA PARA VENDA DE PRODUTOS
antes das operações dos DAOs e finalizadas explicitamente com um commit caso tudo
ocorresse como esperado ou canceladas (rollback) na detecção de algum problema.
Algo importante que não lidamos na nossa implementação é o caso de algum pro-
blema acontecer durante o processo de atualização vários tantos registros na base
de dados. Imagine se o primeiro item de venda foi processado corretamente, mas no
segundo, por algum motivo, aconteceu algum erro como o servidor perdeu a comuni-
cação com o SGBD. Se isso acontecer, teremos dados corrompidos, pois o que deveria
ter sido feito completamente foi feito parcialmente, concorda? Por exemplo, no caso
de inserção/cadastro, o ideal seria iniciarmos uma transação antes de salvar a venda e
dar um “commit” nessa transação antes encaminharmos a requisição para a página
de listagem, ou dar um “rollback” em caso de algum problema acontecer, desfazendo
o que foi feito desde o início da transação, evitando problemas na estrutura da base
de dados que foi modificada. Novamente, para simplificarmos um pouco mais a im-
plementação isso não foi endereçado. Trataremos esse tipo de situação no Capítulo
de persistência, tudo bem?
Vamos agora tratar o lado do cliente.
Front-End
10 <title>Vendas Cadastradas</title>
11
12 <meta charset="UTF-8">
13 <meta name="viewport"
14 content="width=device-width, initial-scale=1.0">
15
16 <link rel="stylesheet"
17 href="${cp}/css/estilos.css"/>
18
19 <script src="${cp}/js/libs/jquery/jquery.min.js"></script>
20 <script src="${cp}/js/formularios/vendas/listagem.js"></script>
21
22 </head>
23
24 <body>
25
26 <h1>Vendas Cadastradas</h1>
27
28 <p>
29 <a href="${cp}/formularios/vendas/novo.jsp">
30 Nova Venda
31 </a>
32 </p>
33
34 <table class="tabelaListagem">
35 <thead>
36 <tr>
37 <th>Id</th>
38 <th>Data</th>
39 <th>Cliente</th>
40 <th>Cancelar</th>
41 </tr>
42 </thead>
43 <tbody>
44
45 <jsp:useBean
46 id="servicos"
47 scope="page"
48 class="vendaprodutos.servicos.VendaServices"/>
49
50 <c:forEach items="${servicos.todos}" var="venda">
51 <tr>
290 CAPÍTULO 8. SISTEMA PARA VENDA DE PRODUTOS
52 <td>${venda.id}</td>
53 <td>
54 <fmt:formatDate
55 pattern="dd/MM/yyyy"
56 value="${venda.data}"/>
57 </td>
58 <td>${venda.cliente.nome} ${venda.cliente.sobrenome}</td>
59 <td>
60 <c:choose>
61 <c:when test="${venda.cancelada}">
62 Cancelada
63 </c:when>
64 <c:otherwise>
65 <a href="#" data-id="${venda.id}"
,→ onclick="cancelarVenda(event,'${cp}')">
66 Cancelar
67 </a>
68 </c:otherwise>
69 </c:choose>
70 </td>
71 </tr>
72 </c:forEach>
73 </tbody>
74
75 </table>
76
77 <p>
78 <a href="${cp}/formularios/vendas/novo.jsp">
79 Nova Venda
80 </a>
81 </p>
82
83 <p><a href="${cp}/index.jsp">Tela Principal</a></p>
84
85 </body>
86
87 </html>
tags poderiam ter sido usadas aqui, mas para manter a consistência na GUI em
relação aos outros cadastros, optei por usar hyperlinks mesmo. Nosso link terá mais
dois atributos definidos. Um você já conhece, que é o onclick, apontando para a
função cancelarVenda(...) . O outro é o atributo data. Esse atributo é interessante
pois podemos armazenar dados nas tags! Para usá-lo, começamos com a palavra
data, inserimos um traço e depois um identificador qualquer, sem espaços ou letras
maiúsculas. Caso queira um identificador com nome composto, ou seja, com mais
de uma palavra, separe-as por traços. No nosso caso, o identificador é id. A ideia
é que esse link tenha um dado associado que é o identificador da venda. A função
cancelarVenda(...) usará esse dado para saber qual venda deve ser cancelada.
Note que também poderíamos ter passado o identificador da venda como argumento
para a função, mas eu quis usar o atributo data para vocês saberem que ele existe.
| Saiba Mais
Mais sobre os atributos data: <https://developer.mozilla.org/en-US/docs/Lea
rn/HTML/Howto/Use_data_attributes>
21 <script src="${cp}/js/formularios/vendas/novo.js"></script>
22
23 </head>
24
25 <body>
26
27 <h1>Nova Venda</h1>
28
29 <form id="formNovaVenda" method="post" action="${cp}/processaVendas">
30
31 <input name="acao" type="hidden" value="inserir"/>
32 <input id="hiddenItensVenda" name="itensVenda" type="hidden"/>
33
34 <div id="divCliente">
35 <jsp:useBean
36 id="servicosC"
37 scope="page"
38 class="vendaprodutos.servicos.ClienteServices"/>
39
40 Cliente:
41 <br/>
42 <select id="selectCliente" name="idCliente" required>
43 <c:forEach items="${servicosC.todos}" var="cliente">
44 <option value="${cliente.id}">
45 ${cliente.nome} ${cliente.sobrenome}
46 </option>
47 </c:forEach>
48 </select>
49 </div>
50
51 <div id="divItensVenda">
52 <table>
53 <tr>
54 <td>
55
56 <jsp:useBean
57 id="servicosP"
58 scope="page"
59 class="vendaprodutos.servicos.ProdutoServices"/>
60
61 <p>
62 Produto:
8.2. CONSTRUINDO O SISTEMA 295
63 <br/>
64 <select id="selectProduto">
65 <c:forEach items="${servicosP.todos}" var="produto">
66
67 <fmt:formatNumber
68 pattern="#.##"
69 minIntegerDigits="1"
70 minFractionDigits="2"
71 maxFractionDigits="2"
72 var="valorVenda"
73 scope="page"
74 value="${produto.valorVenda}"/>
75
76 <option value="${produto.id}"
77 data-valor="${valorVenda}"
78 data-descricao="${produto.descricao}">
79 ${produto.descricao}
80 (R$ ${valorVenda}
81 por
82 ${produto.unidadeMedida.sigla})
83 </option>
84 </c:forEach>
85 </select>
86 </p>
87
88 <p>
89 Quantidade:
90 <br/>
91 <input id="txtQuantidade"
92 type="number"
93 size="3"
94 placeholder="9,99"
95 step="0.01"
96 min="0"/>
97 </p>
98
99 </td>
100 <td class="btnsItensVenda">
101 <p><input id="btnInserir" type="button" value="➕"/></p>
102 <p><input id="btnRemover" type="button" value="➖"/></p>
103 <p><input id="btnLimpar" type="button" value="❌"/></p>
104 </td>
296 CAPÍTULO 8. SISTEMA PARA VENDA DE PRODUTOS
105 <td>
106 Itens da Venda:
107 <br/>
108 <select id="selectItensVenda" size="10" multiple>
109 </select>
110 <br/>
111 <div>
112 <div id="divTotal">Total: R$ 0,00</div>
113 </div>
114 </td>
115 </tr>
116 <tr>
117 <td>
118 </td>
119 <td></td>
120 <td class="alinharDireita">
121 <input id="btnSalvar" type="submit" value="Salvar"/>
122 </td>
123 </tr>
124 </table>
125 </div>
126
127 <a href="${cp}/formularios/vendas/listagem.jsp">
128 Voltar
129 </a>
130
131 </form>
132
133 </body>
134
135 </html>
Você já conhece praticamente todo o código utilizado na Listagem 8.14, sendo assim
focaremos na implementação da funcionalidade apresentado na Listagem 8.15.
1 /**
2 * Implementação das funções do formulário de venda.
3 */
4
5 // Document ready (quando o documento estiver pronto)
6 $( () => {
7
8 // array para armazenar os itens da venda
9 let itensVenda = [];
10
11 // formatadores
12 let fmtMoeda = new Intl.NumberFormat(
13 "pt-BR", {
298 CAPÍTULO 8. SISTEMA PARA VENDA DE PRODUTOS
14 style: "currency",
15 currency: "BRL"
16 }
17 );
18
19 let fmtNumero = new Intl.NumberFormat(
20 "pt-BR", {
21 minimumFractionDigits: 2,
22 maximumFractionDigits: 2
23 }
24 );
25
26 // ao clicar no botão inserir
27 $( "#btnInserir" ).on( "click", event => {
28
29 let $selectProduto = $( "#selectProduto" );
30 let $txtQuantidade = $( "#txtQuantidade" );
31
32 let idProduto = $selectProduto.val();
33 let valorVenda = $selectProduto.find( ":selected" ).data( "valor"
,→ ).toString();
34 let descricao = $selectProduto.find( ":selected" ).data( "descricao"
,→ );
35 let quantidade = null;
36
37 // se o valor da venda tem vírgula, troca por ponto
38 if ( valorVenda.includes( "," ) ) {
39 valorVenda = valorVenda.replace( ",", "." );
40 }
41
42 try {
43 quantidade = new Decimal( $txtQuantidade.val() );
44 } catch ( e ) {
45 }
46
47 if ( quantidade !== null && quantidade.greaterThan( 0 ) ) {
48
49 // há um item da venda igual?
50 let itemIgual = null;
51 itensVenda.some( item => {
52 if ( item.idProduto === idProduto ) {
53 itemIgual = item;
8.2. CONSTRUINDO O SISTEMA 299
96 //se há seleção
97 } else if ( confirm( "Deseja remover o(s) item(ns) da venda
,→ selecionado(s)?" ) ) {
98
99 // itera pela seleção
100 for ( let i = 0; i < selecao.length; i++ ) {
101
102 // busca sequencial nos itens de venda
103 for ( let j = 0; j < itensVenda.length; j++ ) {
104
105 let item = itensVenda[j];
106
107 // encontrou?
108 if ( selecao[i] === item.idProduto ) {
109
110 // remove da posição j
111 itensVenda.splice( j, 1 );
112 break;
113
114 }
115
116 }
117
118 }
119
120 // remonta a lista
121 atualizarGUI();
122
123 }
124
125 });
126
127 // ao clicar no botão limpar
128 $( "#btnLimpar" ).on( "click", event => {
129 if ( confirm( "Deseja remover todos os itens da venda?" ) ) {
130 itensVenda = [];
131 atualizarGUI();
132 }
133 });
134
135 // submissão da venda
136 $( "#formNovaVenda" ).on( "submit", event => {
8.2. CONSTRUINDO O SISTEMA 301
137
138 if ( $( "#selectItensVenda > option" ).length === 0 ) {
139 alert( "Uma venda precisa conter pelo menos um item!" );
140 return false;
141 }
142
143 return true;
144
145 });
146
147 // evita que ao teclar enter dentro do campo
148 // de texto o formulário seja submetido
149 $( "#txtQuantidade" ).on( "keydown", event => {
150 if ( event.keyCode === 13 ) {
151 event.preventDefault();
152 }
153 });
154
155 // constrói as opções do <select> (lista) de itens de venda;
156 // atualiza o valor total da venda;
157 // e prepara os dados para envio
158 let atualizarGUI = () => {
159
160 let $select = $( "#selectItensVenda" );
161 let total = new Decimal( 0 );
162
163 $select.html( "" );
164
165 itensVenda.forEach( item => {
166
167 let valorItem = new Decimal( item.valorVenda )
168 .times( item.quantidade );
169
170 $opt = $( "<option></option>" ).
171 html( `${item.descricao} - ` +
172 `${fmtMoeda.format( item.valorVenda )} x ` +
173 `${fmtNumero.format( item.quantidade )} = ` +
174 `${fmtMoeda.format( valorItem )}` ).
175 val( `${item.idProduto}` );
176
177 $select.append( $opt );
178 total = total.plus( valorItem );
302 CAPÍTULO 8. SISTEMA PARA VENDA DE PRODUTOS
179
180 });
181
182 $( "#divTotal" ).html( "Total: " + fmtMoeda.format( total ) );
183 $( "#hiddenItensVenda" ).val( JSON.stringify( itensVenda ) );
184
185 };
186
187 });
| Saiba Mais
A lista completa dos emojis do Unicode pode ser vista nesse link: <https://unic
ode.org/emoji/charts/full-emoji-list.html>
45 extraímos todos os dados que precisaremos para criar um objeto do item da venda.
Veja que na linha 43 é criado um objeto do tipo Decimal. Esse tipo não é nativo do
JavaScript, mas sim implementado na biblioteca decimal.js inserida no projeto via
CDNJS. Essa biblioteca implementa tipos decimais de precisão arbitrária, assim como
o tipo BigDecimal do Java. Como estamos lidando com quantidades de produtos do
lado do cliente e não queremos arriscar ter algum tipo de erro ou problema relativo à
precisão de números em ponto flutuante, vamos usar essa biblioteca. Entre as linhas
38 e 40 verificamos se o valor da venda, que é tratado como String (linha 33), contém
uma vírgula separador decimal, o que provavelmente deve ser o seu caso, e caso seja,
trocamos por um ponto para podermos criar um objeto do tipo Decimal quando for
necessário.
Se a quantidade informada for um número maior que zero, entraremos no if da
linha 47, ou seja, se uma quantidade válida foi informada o item da venda será criado
ou a quantidade de um item existente será incrementada. Não faz sentido ter valor
negativo em quantidades para o nosso problema, certo?
A primeira coisa que será feita é verificar se já existe um item de venda para o produto
que está se tentando inserir na lista. Nosso banco de dados não permite que possa
existir mais de um item de venda para uma mesma venda e um mesmo produto. Veja
que no banco de dados a chave primária da tabela item_venda é composta pelas
duas chaves estrangeiras, uma de venda e uma de produto. Uma tarefa desse Capítulo
será tornar isso possível. Dada essa restrição, se já houver um item de venda com o
produto que se está tentando inserir, o item de venda que foi inserido posteriormente
será atualizado, ou seja, sua quantidade será incrementada com a nova quantidade
que se está tentando inserir. Para essa verificação, iteraremos sobre os itens da venda
e, caso o identificador do produto do elemento atual for igual ao identificador do
produto que se está tentando inserir na lista, a variável itemIgual receberá esse
item e a iteração parará, por causa do retorno true do callback de some(...) .
Na linha 59, caso o item da venda seja diferente de nulo, ou seja, foi encontrado um
item da venda com o mesmo produto, atualiza-se a quantidade desse item de venda.
Veja que não usamos simplesmente o operador de adição, visto que o valor do atributo
quantidade é do tipo Decimal, havendo a necessidade de usar o método plus na
quantidade do item, passando a quantidade a ser somada e, além disso, o retorno do
método é atribuído à quantidade do item, visto que os objetos do tipo Decimal são
imutáveis.
Se o produto que se está inserindo no novo item de venda não existe na lista, um
novo item da lista será criado e inserido no array entre as linhas 68 e 73. Na linha
76 a lista de itens de venda é montada na GUI, baseando-se nos dados contidos no
array itensVenda , além de outras atualizações na interface gráfica e na linha 77 o
input da quantidade é resetado. A linha 80 contém um alerta que será mostrado caso
304 CAPÍTULO 8. SISTEMA PARA VENDA DE PRODUTOS
se o componente dos itens de venda da linha 160 e na linha 161 cria-se um acumulador
para armazenar o valor total da venda. Limpa-se a lista na linha 163 e itera-se sobre
os itens da venda entre as linhas 165 e 180. Nessa iteração, cada item de venda é
usado para criar uma nova opção para o <select> dos itens de venda. Na linha
167 calcula-se o valor do item que é composto do valor do produto multiplicado pela
quantidade, entre as linhas 170 e 175 é criado um novo item da lista, na linha 177 esse
item é inserido e, na linha 178, o totalizador da venda é incrementado. Ao terminar
esse processo, a lista já estará atualizada com os itens que refletem o array de itens
de venda, faltando atualizar o total da venda, o que acontece na linha 182 e, na linha
183, o array de itens de venda é serializado em JSON para ser enviado na submissão
do formulário usando o campo escondido chamado hiddenItensVenda. Veja que
a serialização em JSON carregará dados que não são necessários como a descrição
do produto e o valor da venda. Poderíamos enviar uma forma mais enxuta desses
dados mantendo um array para os dados completos e um com somente para o que é
necessário para o Servlet atuar, mas deixaremos dessa forma para não complicar mais
do que o necessário nesse momento.
Pronto, terminamos!
8.3 Resumo
Neste Capítulo construímos uma aplicação Web completa para a venda de produtos.
Utilizamos para isso tudo que aprendemos até agora. Com isso você já é capaz de
implementar a maioria dos tipos de cadastros que aparecerão em sistemas do mundo
real. Parabéns! No próximo Capítulo vai ser a sua vez de desenvolver, reescrevendo e
melhorando o sistema de locação de DVDs.
8.4 Projetos
8.5 Desafios
Desafio 8.1: Que tal implementar a paginação das listagens dos cadastros? Imagine
que numa situação real você terá centenas de produtos cadastrados, concorda? Ver
todos esses produtos de uma só vez na interface gráfica pode ser um grande problema,
certo? Você acabou de ser contratato em uma empresa que desenvolveu o sistema
deste Capítulo e seu chefe lhe deu a tarefa de implementar a tal da paginação. Você já
deve ter visto em sistemas reais, veja o destaque em laranja na Figura 8.4. Você deve
tomar todas as decisões necessárias, como quantos registros serão mostrados por
página e deverá procurar como fazer esse tipo de limitação no banco de dados. Boa
sorte!
campo de texto onde o usuário fornecerá um valor que pode tanto ser o identificador
do produto quanto seu código de barras e, ao teclar <ENTER> naquele campo, seja
montada uma lista com os produtos obtidos, algo como mostrado na Figura 8.5. Use
sua imaginação e criatividade para implementar tal funcionalidade!
Desafio 8.3: Seu chefe adorou sua solução para o desafio anterior e agora quer mais
um “botãozinho” no sistema. Entendedores entenderão hahaha! Ele quer que você
implemente a emissão de relatórios para o sistema. Neste primeiro momento ele
quer dois relatórios, um que mostre a quantidade vendida de todos os produtos em
um período de tempo, ou seja, para cada produto, definindo-se uma data inicial e
uma data final em um formulário, realizar a consulta no banco de dados e mostrar
a quantidade que foi vendida de cada um desses produtos. O outro relatório é um
relatório que mostre o montante total das vendas em um período, ou seja, ele quer
saber quanto de dinheiro que entrou em um período de tempo. Você não precisa
usar nada além do que já sabe, mas se quiser empregar o uso de alguma engine de
relatórios como o iReport1 , fique à vontade. Fácil? Difícil? Seu emprego está em jogo!
Mãos à obra!
1
<https://community.jaspersoft.com/project/ireport-designer>
CAPÍTULO
9
S EGUNDO P ROJETO : S ISTEMA PARA
L OCAÇÃO DE M ÍDIAS
Pitágoras
N
ESTE Capítulo final aplicaremos o conhecimento adquirido até o mo-
mento na construção de uma aplicação Web em Java totalmente funci-
onal, terminando o desenvolvimento do sistema de locação de DVDs
começado no Capítulo 6, alterando diversas coisas que já foram feitas,
inserindo a possibilidade do cadastro de mídias de vários tipos e, enfim, a locação
de uma ou mais mídias por cada cliente. Sim, eu sei que locadora de DVDs, BluRays
etc. não são mais tão populares, mas acredito que você saiba o que são e é um bom
exemplo para podermos colocar o que sabemos em prática.
9.1 Introdução
Assim como no Capítulo 6, neste Capítulo será apresentada uma série de requisitos,
de forma muito simplificada, que deve ser usada para criar uma aplicação Web da
mesma forma que fizemos no Capítulo 8. Novamente, tudo que será requisitado estará
309
310 CAPÍTULO 9. SEGUNDO PROJETO: SISTEMA PARA LOCAÇÃO DE MÍDIAS
As entidades e seus atributos você poderá ver no diagrama apresentado na Figura 9.1.
Cada um dos cadastros base, ou seja, cadastro de mídias e seus exemplares, ato-
res/atrizes, gêneros, classificações etárias, tipos, classificações internas, clientes, ci-
dades e estados, deve conter as funcionalidades de criar, alterar e excluir um deter-
minado registro. A página principal da aplicação deve conter um link para cada tipo
9.3. DESENVOLVIMENTO DO PROJETO 311
de cadastro. Cada mídia pode ter um ou mais exemplares. O valor da locação virá da
classificação interna de uma mídia, por exemplo, quando um filme ou jogo é lança-
mento, a locação é mais cara, concorda? Abaixo de cada nova tabela há uma caixa
de texto com alguns exemplos do que poderia haver naquele cadastro para que você
possa se guiar. O processo de locação é análogo ao processo de venda de produtos do
Capítulo 8 e aqui a locação é por exemplar, então a tabela de itens de locação já está
correta para a modelagem da solução desse problema, pois não será possívei alugar o
mesmo examplar duas vezes na mesma locação.
9.4 Resumo
Neste Capítulo foi requisitado que você implementasse uma aplicação Web em Java
para gerenciar a locação de mídias como BluRays e DVDs, por isso, não há atividades
a serem realizadas.
B IBLIOGRAFIA
313