Prévia do material em texto
PROGRAMAÇÃO
ORIENTADA A OBJETOS III
PROGRAMAÇÃO
ORIENTADA A OBJETOS III
Copyright © UVA 2020
Nenhuma parte desta publicação pode ser reproduzida por qualquer
meio sem a prévia autorização desta instituição.
Texto de acordo com as normas do Novo Acordo Ortográfico
da Língua Portuguesa.
AUTORIA DO CONTEÚDO
Carlos Frederico Motta Vasconcelos
REVISÃO
Janaina Vieira
Theo Cavalcanti
PROJETO GRÁFICO
UVA
DIAGRAMAÇÃO
UVA
V331 Vasconcelos, Carlos Frederico Motta.
Programação orientada a objetos III [recurso eletrônico] / Carlos Frederico
Motta Vasconcelos. – Rio de Janeiro: UVA, 2021.
1 recurso digital (6474 KB)
Formato: PDF
ISBN 978-65-5700-091-5
1. Programação orientada a objetos (Computação). 2. Banco de dados
relacionais. 3. Java (Linguagem de programação de computador). I. Universidade
Veiga de Almeida. II. Título.
CDD – 005.11
Bibliotecária Adriana R. C. de Sá CRB 7 – 4049.
Ficha Catalográfica elaborada pelo Sistema de Bibliotecas da UVA.
SUMÁRIO
Apresentação
Autor
6
7
Acesso a bancos de dados relacionais via JDBC
utilizando diferentes padrões de projeto
37
• Acesso remoto a gerenciadores de bancos de dados relacionais com
uso do JDBC
• Técnicas de acesso concorrente a bancos de dados cliente-servidor
• Tratamento de erros em bancos de dados
UNIDADE 2
8
• Padrões de arquitetura para desenvolvimento de sistemas Orientados
a Objetos – OO
• Padrões de projeto de sistemas OO: Modelo-Visão-Controle – MVC,
DAO, Factory e Singleton
• Acesso e conexão a gerenciadores de banco de dados com uso do JDBC
Padrões de arquitetura e padrões de projetos Orientados
a Objetos para desenvolvimento de sistemas Java
UNIDADE 1
SUMÁRIO
JavaServer Faces 120
• Visão geral do JavaServer Faces – JSF
• Managed Beans, requisições e navegação
• Componentes básicos, validação de campos, PrimeFaces e Ajax JSF
UNIDADE 4
81
• Ferramentas de mapeamento objeto-relacional – MOR (JPA e Hibernate)
• Pool de Conexões, Lazy Load e Relacionamentos
• Gerenciador de Estados, Caching e consultas JPQL
Persistência de dados via JPA e Hibernate
(MOR automático)
UNIDADE 3
6
Nesta disciplina, serão abordadas técnicas para o desenvolvimento de sistemas compu-
tacionais Web usando a linguagem de programação Java e para realizar conexão com
gerenciadores de banco de dados relacionais via Java DataBase Connectivity – JDBC.
Além da biblioteca JBDC para armazenamento (persistência) em bancos de dados re-
lacionais, nesta disciplina serão apresentadas ferramentas do tipo object relational ma-
pping – ORM, que facilitam o uso do JDBC para acesso a bancos de dados relacionais.
Além disso, serão abordadas ferramentas (frameworks) para facilitar o desenvolvimento
e a manutenção de aplicações Web.
Para auxiliar o desenvolvimento de sistemas com maior complexidade, também será ne-
cessário conhecer alguns padrões de projeto de Orientação a Objeto específicos. Serão
apresentados padrões de projeto que possuem caraterísticas próprias que podem faci-
litar a implementação e a manutenção de sistemas de informação, tais como: padrões
de projeto de arquitetura em multicamadas, Model-View-Controller – MVC, Data Access
Objects – DAO, Construtor Virtual – Factory Method, e Singleton.
Por fim, apresentaremos ferramentas de desenvolvimento de sistemas computacionais
orientados a objetos com persistência de dados via Hibernate (ORM Automático) e tam-
bém será abordada a utilização do framework JavaServer Faces – JSF para adicionar
comportamento dinâmico às páginas das aplicações Web.
Atualmente o mercado apresenta inúmeras oportunidades aos profissionais de Tecnolo-
gia da Informação que possuem conhecimentos sobre o desenvolvimento de sistemas
Web com a utilização da tecnologia Java e seus respectivos componentes. Por sua vez, o
Java possui inúmeras vantagens, como maior liberdade de desenvolvimento de sistemas
de forma independente de fabricantes de softwares, sistemas operacionais, banco de
dados, servidores de aplicação e containers.
APRESENTAÇÃO
7
CARLOS FREDERICO MOTTA VASCONCELOS
Possui graduação em Engenharia Eletrônica pela Universidade Federal do Rio de Janei-
ro – UFRJ e mestrado em Engenharia Biomédica pelo Instituto Alberto Luiz Coimbra de
Pós-Graduação e Pesquisa de Engenharia – COPPE/UFRJ na área de Processamento de
Sinais. Possui MBA em Sistemas de Telecomunicações pelo IBMEC-RJ.
Experiência de 17 anos como professor do curso de Ciência da Computação na Univer-
sidade Veiga de Almeida – UVA nas áreas de Computação Gráfica, Processamento de
Sinais e Imagens, Interfaces Homem-Máquina, Multimídia e Programação de Disposi-
tivos Móveis.
Possui experiência de 18 anos no planejamento e otimização de sistemas usados em
redes celulares de telecomunicações com tecnologias LTE, UMTS, GSM e CDMA. Expe-
riência no desenvolvimento de sistemas de geoprocessamento (GIS) e de simulação de
sinais de radiofrequência.
Atualmente trabalha como analista em Ciência e Tecnologia na área de Engenharia Clíni-
ca do Instituto Nacional do Câncer, do Ministério da Saúde, exercendo atividades técnicas
especializadas na área de gerenciamento do parque de equipamentos médico-hospita-
lares de média e alta complexidades, além da fiscalização dos contratos de manutenção
dos equipamentos e sistemas de informação. Experiência em elaboração de especifica-
ções e documentação técnicas necessárias aos processos de aquisição, recebimento e
aceitação de novos equipamentos e sistemas de informação.
AUTOR
Padrões de arquitetura e
padrões de projetos Orientados
a Objetos para desenvolvimento
de sistemas Java
UNIDADE 1
9
Para o desenvolvimento de sistemas computacionais de maior complexidade torna-se
necessário conhecer e aplicar padrões de projeto específicos para auxiliar as etapas ini-
ciais de especificação, análise e design de tais sistemas, com o objetivo de organizar as
etapas seguintes de implementação do código. Nesta unidade serão abordados alguns
exemplos de padrões de projeto e de arquitetura bastante utilizados no desenvolvimento
de sistemas Orientados a Objetos – OO envolvendo armazenamento em banco de dados
e aplicações Web.
INTRODUÇÃO
Nesta unidade você será capaz de:
• Compreender diferentes padrões de arquitetura e projeto (MVC, DAO, Factory e
Singleton) para desenvolvimento de sistemas OO com acesso a bancos de dados
e aplicações Web.
OBJETIVO
10
Padrões de arquitetura para desenvolvimen-
to de sistemas Orientados a Objetos – OO
A construção de sistemas computacionais complexos exige a implementação de uma
quantidade muito grande de componentes. Antes de iniciar as etapas de programação
desses componentes é necessário definir as fases iniciais para descrição dos requisitos
do cliente, especificações do sistema, juntamente com a definição dos modelos de pro-
jeto e arquitetura a serem usados no desenvolvimento do sistema computacional.
Observe a figura a seguir. A imagem mostra um exemplo de um modelo básico de proje-
to de um sistema computacional até a etapa de implementação de código:
Figura: Modelo básico de projeto de um sistema computacional.
Fonte: O autor (2020).
Um dos desafios das equipes de projeto de sistemas computacionais complexos é con-
seguir uma forma de organizar os diversos componentes paraque seja possível:
• Atender aos requisitos funcionais e não funcionais definidos na especificação.
• Reusar componentes de forma a não implementar o mesmo serviço mais de uma vez.
Mundo real Mundo computacional
Requisitos do sistemaRequisitos do cliente
Especificação técnica
Modelos de arquitetura
Modelos de projeto
Modelos de design
Implementação de código
11
• Facilitar a manutenção do sistema (troca de componentes, mudança de regras,
entre outros).
• Trazer portabilidade para o sistema (execução em plataformas de hardware/
software distintas).
Nesse contexto, a elaboração de sistemas de informação passa pela compreensão e
pela aplicação adequada de modelos de arquitetura e padrões de projeto. O domínio des-
ses conceitos e de sua aplicação desempenha um papel fundamental no gerenciamento
da complexidade inerente ao software a ser desenvolvido.
Podemos exemplificar o ciclo de vida de um sistema por meio do modelo em cascata,
inicialmente proposto por W. W. Royce em 1970. As etapas do modelo em cascata são
mostradas na figura a seguir.
Figura: Etapas do modelo em cascata.
Fonte: O autor (2020).
Definição dos requisitos
Análise
Implementação
Implantação
Projeto
Teste/Avaliação
Manutenção
12
Apesar de simplificado, o modelo em cascata nos mostra que, antes da etapa de im-
plementação do código do sistema, é necessário que as etapas iniciais de definição de
requisitos, análise e design do projeto sejam cuidadosamente elaboradas. Dessa forma:
A escolha de padrões de arquitetura e de design de projeto adequados facilitará as
etapas seguintes de implementação da programação, testes, alterações e manuten-
ções necessárias no sistema durante sua implantação.
Existem diferentes modelos propostos para auxiliar as etapas de análise e projeto, como
modelos em cascata, modelos incrementais, prototipagem, entre outros. Atualmente um
dos modelos mais difundidos para desenvolvimento de sistemas OO é o Processo Unifi-
cado (Unified Process – UP).
O Processo Unificado define as atividades específicas voltadas para análise,
arquitetura e design de sistemas.
Agora, antes de continuarmos, vamos refletir:
Por que devemos nos preocupar em definir um modelo de arquitetura
adequado?
A definição do modelo de arquitetura é importante para:
APOIAR o
planejamento do
desenvolvimento
do sistema.
PERMITIR melhor
tratamento dos
atributos de
qualidade de software
(requisitos não
funcionais).
AJUDAR no
gerenciamento dos
riscos técnicos e da
complexidade da
solução.
APOIAR a análise
de impacto
de mudanças
tecnológicas e
de negócio.
ORIENTAR as
atividades de projeto
e implementação.
13
O que são componentes arquiteturais?
São elementos que executam ou apoiam a execução das funcionalidades do sistema
computacional, ou seja, são responsáveis pela execução dos serviços propriamente ditos.
Componentes também podem ser definidos como abstrações que representam o
hardware e o software (desenvolvido pela equipe ou por terceiros), que fazem parte de
um sistema computacional.
Outra definição importante são os Conectores Arquiteturais, que definem a interação
entre os componentes da arquitetura, permitindo representar a comunicação, coordena-
ção e cooperação entre eles. Conectores podem representar a conexão entre componen-
tes de hardware (equipamentos) ou entre componentes de software.
Os conectores podem ser usados para representar:
• Comunicação de dados (HTTP, HTTPS, TCP/IP, SSL etc.).
• Chamada de funções, procedimentos ou métodos.
Exemplos de componentes de software:
• Desenvolvidos pela equipe (aplicativos, pacotes, módulos, bibliotecas,
web services etc.).
• Sistemas Gerenciadores de Banco de Dados – SGBD (SQL Server, MySQL,
Oracle etc.).
• Servidores de aplicação (Apache Tomcat, Zend Server, IIS etc.).
• Containers (Docker, Kubernetes, Google Cloud, Microsoft Azure etc.).
• Sistemas operacionais (Windows, Linux, Android, iOS etc.).
• Bibliotecas de terceiros (JQuery, Apache Commons, OpenGL etc.).
Exemplos de componentes de hardware:
• Servidores.
• Computadores pessoais (desktop, notebook etc.).
• Dispositivos móveis (smartphone, tablet, smartwatch etc.).
• Sensores/Leitores.
• Atuadores (equipamentos diversos controlados por software).
Exemplo
14
• Invocação de serviços.
• Acesso a dados.
• Conexão com banco de dados (ODBC, JDBC, ADO.NET etc.).
• Conexão com arquivos (file input/output).
• Streamings, eventos etc.
Existem diversos modelos arquiteturais propostos na literatura. Contudo, nesta disci-
plina vamos apresentar os modelos de camadas (layers), cliente-servidor (2-tier) e de
multicamadas N-Tier.
Modelo em camadas
O modelo de arquitetura em camadas caracteriza-se pela organização do sistema em
um conjunto de camadas lógicas, em que cada camada (“layer”) oferece um conjunto
de serviços e possui uma função bem definida no sistema. Outro conceito importante é
que uma camada somente solicita serviços da camada inferior e fornece serviços para
a camada superior.
Figura: Exemplo de modelo em camadas de um sistema computacional de vendas
com persistência de dados.
Fonte: O autor (2020).
Apresentação
Controle
Negócio ou
Domínio
Persistência
de dados
Formulário
de Pedido
Controle
do Pedido
PedidoDAO
Pedido
ClienteDAO
Cliente
BD
15
O modelo em camadas oferece vantagens como:
• Possibilidade de desenvolver cada camada de forma independente (paralelismo).
• As camadas podem ser facilmente substituídas por equivalentes.
• Mudanças em uma camada só impactam a camada imediatamente superior.
Por outro lado, o modelo em camadas possui as seguintes desvantagens:
• Cuidados extras devem ser tomados para garantir a integridade entre as cama-
das; por exemplo, o estado de um objeto na camada de persistência deve ser refleti-
do na camada de domínio e também na camada de apresentação.
• Muitas camadas podem comprometer o desempenho do sistema, pois a requisi-
ção precisa trafegar pelas várias camadas até ser atendida.
Modelo cliente-servidor (2-tier)
O modelo arquitetural cliente-servidor possui as seguintes características:
• Ser organizado como um conjunto de serviços (servidores e clientes dos serviços).
• Cliente e servidor rodam em máquinas distintas.
• Requer uma estrutura de rede para clientes acessarem os servidores remotamente.
• Clientes devem saber quais serviços e servidores estão disponíveis, mas os servi-
dores não conhecem seus clientes.
• Baseado no protocolo “pergunta-resposta” (request-reply), quando um cliente faz
um pedido ao servidor e espera pela resposta. O servidor executa o serviço e res-
ponde ao cliente.
É importante destacar que “tiers” se referem a camadas físicas.
E oferece vantagens como:
• A distribuição de dados é fácil e direta.
• Possibilita hardware mais barato (clientes leves).
• Facilidade de adicionar novos servidores ou atualizar servidores existentes.
16
E possui as seguintes desvantagens:
• Pode haver redundância de serviços em diferentes servidores.
• Não prevê um registro central de serviços, ou seja, os clientes devem saber onde
estão os servidores e quais serviços eles disponibilizam.
• Precisa organizar três camadas lógicas (apresentação, negócio e dados) em duas
camadas físicas (cliente e servidor).
Modelo multicamadas (N-tier)
O modelo multicamadas (N-tier) pode ser considerado uma evolução da arquitetura
cliente-servidor porque consegue separar as lógicas de apresentação, negócio e dados
em máquinas distintas.
E oferece vantagens como:
• Melhor balanceamento de carga entre as diversas camadas.
• Aumenta a escalabilidade: é fácil adicionar novos servidores de aplicação quando
o número de usuários aumenta.
E possui as seguintes desvantagens:
• Maior complexidade no desenvolvimento para um número maior de camadas
(quatro ou mais).
• Quanto maior o número de camadas, maior o overhead de comunicação entre elas.
Figura: Exemplos de modelo de arquitetura 3-Tier.
Fonte: O autor (2020).
Servidor
Processamento de Aplicações
Servidor
Gerenciamentode dados
Servidor Web
Processamento de Aplicações
Servidor de BD
Gerenciamento de dados
Apresentação
HTTP
Cliente
Cliente
Cliente
Cliente
17
Figura: Exemplo de modelo de arquitetura 4-Tier.
Fonte: O autor (2020).
Arquitetura de Microsserviços
O conceito de Arquitetura de Microsserviços surgiu há poucos anos como uma forma
de desenvolver aplicações de software. Apesar de não existir ainda uma definição clara
do padrão de Microsserviços, existem algumas características comuns em torno da ca-
pacidade de negócio, distribuição automatizada, inteligência nos pontos de integração e
controle descentralizado das linguagens e dados.
Segundo Martin Fowler (2015), o padrão arquitetural baseado em Microsserviços corres-
ponde ao modo de desenvolver uma única aplicação como um conjunto de pequenas
aplicações, cada uma em seu próprio contexto e com processos independentes que se
comunicam por meio de mecanismos simples, tradicionalmente por requisições a uma
API utilizando o protocolo HTTP.
Uma das principais razões para utilizar serviços como componentes é que serviços po-
dem ser implantados independentemente uns dos outros. Assim, a partir do baixo aco-
Camada do cliente
(browser)
Camada da aplicação
(Servlet/JSP)
Camada do negócio
(Casses Java/Java Beans)
Camada de dados
(SGBD)
18
plamento e da decomposição da aplicação em vários serviços, a manutenção ou alte-
ração de um serviço não atinge outras partes da aplicação no que em geral ocupa um
tempo de indisponibilidade menor.
19
Padrões de projeto de sistemas OO: Modelo-
-Visão-Controle – MVC, DAO, Factory e
Singleton
Neste tópico serão abordados alguns padrões de projeto (ou padrões de design) para
orientar o desenvolvimento de sistemas computacionais Orientados a Objetos – OO
complexos com acesso a banco de dados e aplicações Web.
O paradigma da Programação Orientada a Objetos tenta resolver alguns dos problemas
existentes na programação procedural, como permitir maior reutilização de código e fa-
cilitar a manutenção, a modificação e a evolução dos sistemas.
Como definir as responsabilidades de cada componente ou classe de um
sistema OO para alcançar uma alta coesão?
É necessário definir componentes com atribuições bem delimitadas a fim de facilitar seu
gerenciamento. Caso contrário, as classes serão difíceis de compreender, reutilizar e rea-
lizar manutenções ou modificações durante o ciclo de vida do sistema.
Vamos pensar!
Como implementar os diversos componentes do sistema OO para que se
tenha baixo acoplamento? Como projetar componentes, classes e sub-
sistemas para que as variações nesses elementos não tenham um im-
pacto indesejável no restante do sistema?
O conceito de coesão está relacionado ao princípio da Responsabilidade Única
usado em projeto de sistema OO, em que idealmente uma classe deve ter ape-
nas uma responsabilidade e realizá-la de maneira adequada. Uma classe com
baixa coesão assume muitas responsabilidades, realiza muitas coisas não re-
lacionadas e trabalha demais. Como consequência, classes com baixa coesão
apresentam maior dificuldade de manutenção, modificação e reúso.
Importante
20
De forma geral, a área de Projeto de Sistemas OO possui alguns princípios e recomen-
dações para a organização de componentes, suas responsabilidades e relacionamentos,
de forma que tais componentes sejam mais flexíveis e que as mudanças em um deles
tenham menor impacto possível nos demais. Além disso, que sejam mais fáceis de en-
tender e à base de componentes que possam ser usados em vários sistemas, facilitando
o reúso do código.
Padrão Model-View-Controller – MVC
O padrão MVC pode ser considerado tanto um padrão de arquitetura como de projeto. Ele
possui como características a divisão da aplicação interativa em três partes:
• Model: encapsula os dados e as funcionalidades do negócio e é independente de
uma apresentação específica.
• View: apresenta informações do Model ao usuário.
• Controller: trata a entrada do usuário.
Além disso, no padrão MVC podem existir múltiplas Views para um mesmo Model e cada
View está associada a um único Controller. Contudo, os dois juntos, Views e Controllers,
formam a interface com o usuário.
O padrão MVC é normalmente utilizado em sistemas interativos quando existem várias
maneiras de visualizar e interagir com os dados. Outro uso importante do MVC é quando
os requisitos de interação com os usuários são voláteis ou em aplicações que requerem
interfaces em diversas plataformas distintas de hardware/software.
O MVC estabelece um mecanismo para propagação das mudanças do projeto de forma
a manter a consistência entre Model e View. A saber:
• Os Controllers recebem a entrada do usuário (geralmente eventos).
O termo “acoplamento” é uma medida de quanto um elemento está conectado
a ou depende de outros elementos. Um componente com acoplamento forte
depende de muitos outros componentes, o que dificulta a reutilização, a manu-
tenção e as modificações no código.
Ampliando o foco
21
• Eventos são traduzidos em requisições de serviço que são enviadas para o Model
ou para a View.
• Após receber a requisição e alterar seu estado interno o Model notifica todas as
Views a ele conectadas.
• As Views notificadas recuperam os dados do Model e atualizam as informações
para o usuário.
Figura: Modelo MVC para desenvolvimento de aplicações Web.
Fonte: O autor (2020).
Podemos citar como vantagens do padrão MVC:
• Permite diferentes padrões/estilos de interação sem alterar o núcleo da aplicação.
• Permite a visualização da mesma informação em formatos distintos, porém sin-
cronizados.
• Permite a alteração de Views/Controllers em “tempo de execução”.
E como desvantagens:
• Maior complexidade quando o modelo de dados e de interações é muito simples.
• Views e Controllers fortemente interconectados.
Model
Enviar requisição
Apresentar dados
forward
model.alterarEstado()
model.getDados()
1 2a
2b
4 3
View
Controller
22
Figura: Exemplo do MVC usado no desenvolvimento de aplicações Web com JSP/Servlet/EJB/JPA.
Fonte: O autor (2020).
Padrão Factory Method
Muitas vezes o processo de criação de um objeto exige condições específicas que não
são apropriadas para serem incluídas dentro do método construtor do objeto. Pode ocor-
rer que para a criação de um objeto sejam necessárias informações externas, respon-
sabilidades adicionais, duplicação indesejada de código, gerando maior acoplamento e
menor coesão na classe.
O padrão Factory Method pode definir uma interface para criação de um objeto, mas
deixar que as subclasses decidam qual classe instanciar.
Esse padrão permite que uma classe delegue a responsabilidade de instan-
ciação às subclasses.
Dada uma hierarquia de classe, temos um ponto do código em que gostaríamos de cons-
truir um objeto dessa hierarquia. Entretanto, o objeto a ser construído dependerá de algu-
ma condição.
Model
Sessions Beans
(EJB)
Entity Classes
(JPA)
Request
Response
Database
Controller
(servlet)Client
(browser)
View
(JSP pages)
23
Figura: Exemplo do padrão Factory Method.
Fonte: O autor (2020).
Padrão de projeto Data Access Objects – DAO
Grandes sistemas computacionais normalmente precisam armazenar suas informações
de trabalho de forma organizada para permitir consultas, alterações de registros ou
geração de relatórios de maneira eficiente. Dessa forma, geralmente é necessário usar
uma solução de banco de dados, que armazena as informações de forma organizada e
prontas para consultas.
Grande parte dos bancos de dados comerciais são relacionais, compostos por uma es-
trutura de tabelas e atributos que possuem relacionamentos entre si.
Exemplo 1
Precisamos criar um objeto que armazene a forma de pagamento de um pedi-
do, que pode ser cartão de crédito, débito, boleto ou PayPal, a depender do tipo
desejado pelo cliente.
Exemplo 2
Precisamos criar um objeto que armazene a forma de conexão em um banco
de dados, que pode ser MySQL, SQLServer, Oracle.
Exemplo
24
O modelo debanco de dados relacionais possui um padrão de trabalho bem
diferente do paradigma da Orientação a Objetos.
Por exemplo, para começar a trabalhar com banco de dados relacionais é necessário
conhecer a linguagem SQL (Structured Query Language), que é usada para a criação de
tabelas, conexões aos bancos, realização de consultas, entre outras ações.
Dessa maneira será necessário:
Desenvolver classes para trocar informações com os Sistemas Gerenciadores dos
Bancos de Dados – SGBD e implementar operações CRUD (Create, Read, Update e
Delete).
Essas classes devem ser capazes de:
• Ler e escrever nas tabelas do banco de dados.
• Transformar esses dados em objetos ou lista de objetos.
• Realizar operações usando instruções SQL e outras funcionalidades.
Porém, teremos um problema. Quando é preciso colocar código SQL dentro das classes
Java para incluir funcionalidades de acesso e consulta aos bancos de dados, as reco-
mendações de alta coesão e baixo acoplamento não são mais respeitadas. O código fica
menos legível, mais confuso e apresenta maior dificuldade para alterações e manutenção.
Como podemos solucionar esse problema?
A solução para esse problema é tentar separar o código SQL de acesso ao banco de
dados das demais classes de lógica e colocá-lo em uma classe responsável apenas pelo
acesso aos dados. Dessa forma, o código de acesso ao banco de dados fica concentra-
do em apenas um local, tornando mais fácil a manutenção, além de tornar as classes
mais legíveis.
Dessa forma, o padrão de projeto Data Access Object – DAO permite que, por meio de
uma única classe, seja realizada toda a lógica de controle de acesso ao banco de dados,
separando a lógica de negócio das outras classes.
25
Somente a partir das classes DAO é que será possível:
1. Conectar e acessar o banco de dados.
2. Manter os princípios de alta coesão e baixo acoplamento do código.
O padrão DAO permite que seja possível alterar a forma de persistência de dados sem in-
fluenciar a lógica de negócio. Nas próximas unidades serão abordados maiores detalhes
sobre a implementação das classes DAO.
Figura: Exemplo de Diagrama de Classes sem DAO – Objeto está com responsabilidades demais.
Fonte: O autor (2020).
Figura: Exemplo de Diagrama de Classes com DAO – Agora apenas o ObjetoDAO possui responsabilida-
des CRUD – Maior coesão e menor acoplamento.
Fonte: O autor (2020).
26
Padrão Singleton
Em diversas situações é necessário que apenas uma instância de uma classe seja cria-
da, independentemente de quantas requisições de criação possam ser feitas.
Veja alguns exemplos.
O padrão Singleton é definido de forma a garantir que uma classe só tenha uma única
instância e prover um ponto de acesso global a ela.
Figura: Exemplo de Diagrama de classe do Padrão Singleton.
Fonte: O autor (2020).
Observe o exemplo a seguir de implementação Java do padrão Singleton com instancia-
mento direto.
• Uma única conexão com o banco de dados.
• Um único arquivo de log de erros.
• Uma única fila de impressão.
• Um único arquivo de configuração.
Exemplos
27
No exemplo anterior o construtor é definido como “private” para impedir que a classe
Singleton seja instanciada fora dela. Foi criado um atributo público e estático (da classe)
que retorna a partir de um método estático (getInstancia), uma única instância dessa
classe. Como o método “getInstancia()” é estático ele pode ser chamado de outra classe
sem precisar instanciar a classe Singleton. É importante notar que neste exemplo a ins-
tância da classe será sempre criada mesmo antes de chamar o método “getInstancia()”.
public class Singleton {
private static Singleton instancia = new Singleton();
private Singleton() {
}
public static synchronized Singleton getInstancia() {
return instancia;
}
}
Exemplos
28
Acesso e conexão a gerenciadores de banco
de dados com uso do JDBC
O desenvolvimento de sistemas de informação para empresas exige funcionalidades de
armazenamento de dados, também conhecidas como persistência de dados. Em geral,
as atividades de controle e gestão de um sistema empresarial necessitam de acesso às
informações armazenadas para geração de relatórios e gráficos, histórico de problemas,
análise de tendências, planejamento, tomada de decisões, entre outras diversas atividades.
Dessa forma, uma aplicação Java precisa realizar uma conexão com um Sistema Geren-
ciador de Banco de Dados – SGBD para poder implementar as operações necessárias
para a manutenção de informações, tais como: inclusão, leitura, alteração e exclusão
de dados. Essas operações também são conhecidas pelo acrônimo:
C reate
R ead
U pdate
D elete
Para realizar a conexão devemos inicialmente identificar o gerenciador de banco de da-
dos que será utilizado pelo projeto, já que para cada tipo de gerenciador existe um driver
(modo de acesso próprio). Logo, para cada gerenciador de banco de dados devemos
utilizar uma classe específica para esse fim.
Conexão a banco de dados em Java
A biblioteca de persistência em banco de dados relacionais do Java é chamada
Java DataBase Connectivity – JDBC e fica dentro do pacote java.sql.
A biblioteca JDBC consiste em um único conjunto de interfaces muito bem
definidas e cujos métodos e classes devem ser implementados para permitir
a conexão e o acesso ao banco de dados.
Essa configuração com uma API única padroniza a forma de acesso aos dados armaze-
nados, evitando que cada banco de dados tenha sua própria interface.
29
Entre as diversas interfaces do JDBC, podemos citar a interface Connection, na qual são
definidos métodos padrões para trabalhar com banco de dados, tais como:
• Executar consultas.
• Inserir dados.
• “Comitar” uma transação.
• Fechar a conexão, entre outros.
Caso seja necessário trabalhar com algum banco de dados específico, como o MySQL,
precisaremos de classes concretas que implementem essas interfaces do pacote java.sql.
Esse conjunto de classes concretas recebe o nome de driver e fará a ligação entre a apli-
cação que usa a interface JDBC e o banco de dados. Esse conjunto de classes é o que
implementa a comunicação a partir do protocolo proprietário do banco de dados.
Vamos entender melhor o assunto! Veja o exemplo a seguir.
Para abrir uma conexão com um determinado banco de dados precisamos utilizar sem-
pre o driver apropriado. A classe DriverManager do JDBC é a responsável por comunicar-
-se com os drivers disponíveis para conexão, como mostrado na figura a seguir:
Todos os principais bancos de dados possuem drivers JDBC para utilização
com Java. O nome “driver” é análogo ao utilizado para periféricos em geral.
Como um sistema operacional poderia comunicar-se com as diversas impresso-
ras disponíveis no mercado, cada uma com características específicas?
Neste caso, é necessário que cada impressora disponibilize um driver com as
interfaces necessárias para realizar a comunicação com o sistema operacional.
Exemplo
30
Figura: Modelo de conexão a diferentes banco de dados com JDBC API e Driver Manager.
Fonte: tutorialspoint.com. Adaptado.
Conexão implementada por meio do método estático getConnection com uma String
que indica a qual banco desejamos nos conectar. Essa String é chamada de string de
conexão JDBC, conforme mostra o exemplo a seguir:
DriverManager.getConnection(string_de_conexao);
Para conexão ao banco de dados MySQL a string de conexão tem o seguinte formato:
jdbc:mysql://ip/nome_do_banco
MySQL SQL
Server
Oracle
Java Application
JDBC API
JDBC Driver
Manager
Drivers
específicos
JDBC DriverJDBC Driver JDBC Driver
https://www.tutorialspoint.com/jdbc/jdbc-introduction.htm
31
Devemos substituir:
• “ip” pelo código IP da máquina do servidor.
• “nome_do_banco” pelo nome do banco de dados a ser utilizado.
Na string de conexão também podem ser incluídos o login e a senha do usuário.
Agora, veja um exemplo prático. Preste atenção!
Utilizando as informações anteriores podemos usar o código a seguir para implemen-
tar a conexão paraum banco de teste MySQL (“BD_teste”), caso ele esteja rodando na
mesma máquina:
public class JDBCExemplo {
public static void main(String[] args) throws SQLException {
Connection conexao =
DriverManager.getConnection(“jdbc:mysql://localhost/BD_teste”);
System.out.println(“Conectado!”);
conexao.close();
}
}
No exemplo anterior o tratamento de exceções não está sendo realizado de forma ade-
quada. Estamos deixando passar a SQLException, uma “exception checked”, que pode
ser lançada por muitos dos métodos da API de JDBC.
Em uma aplicação real é necessário fazer um tratamento de exceções mais específico
utilizando a estrutura “try/catch” nos lugares em que há possibilidade de recuperação
de alguma falha com o banco de dados. Vale lembrar também que é necessário fechar
todas as conexões que foram abertas.
32
Ao testar esse código, será recebida uma “exception” informando que a conexão não
pôde ser aberta, de acordo com a mensagem a seguir:
java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost/BD_teste
Esse problema ocorreu porque o sistema ainda não achou uma implementa-
ção de driver JDBC que possa ser usada para abrir a conexão indicada pela URL
jdbc:mysql://localhost/BD_teste.
O próximo passo é adicionar o driver do MySQL ao “classpath”, ou seja, o arquivo “.jar”
contendo a implementação JDBC do MySQL (MySQL Connector) precisa ser colocado
em um lugar visível pelo seu projeto ou adicionado à variável de ambiente CLASSPATH.
Class.forName
Até a versão 3 do JDBC, antes de chamar o DriverManager.getConnection() era necessá-
rio registrar o driver JDBC que iria ser utilizado a partir do método:
Class.forName(“com.mysql.jdbc.Driver”)
No caso do MySQL, que carregava essa classe e se comunicava com o DriverManager,
a partir do JDBC 4 (presente no Java 6) esse registro passou a não ser mais necessário.
Vale lembrar que, se o JDBC for usado em um projeto com Java 5 ou anterior, será ne-
cessário fazer o registro do driver JDBC carregando sua classe, que se registrará no
DriverManager.
Alterando o banco de dados
Inicialmente, para trocar de um banco de dados para outro, bastaria modificar a string de
conexão e a string de registro do driver JDBC. O problema é que os códigos implementa-
dos das classes JDBC utilizam código SQL para acesso aos bancos de dados.
33
Um banco de dados diferente pode utilizar uma implementação da linguagem SQL não
totalmente compatível com o padrão ANSI SQL. Dessa forma, apenas alterar as strings
de conexão não garante o total funcionamento das classes no novo banco de dados. Nas
próximas unidades serão abordadas soluções para esse problema, como é o caso do
Hibernate (www.hibernate.org) e da especificação Java Persistence API – JPA.
Para ampliar o seu conhecimento veja o material complementar da Unidade 1,
disponível na midiateca.
MIDIATECA
Algumas vezes é necessário realizar conexões com diferentes bancos de dados,
cada um com seu driver específico. Segue exemplo de método com uma confi-
guração básica para a conexão com um servidor de banco de dados MySQL. O
método retornará a conexão com o banco MySQL por meio do objeto con:
• Biblioteca: mysql-connector-java-5.1.6-bin.jar.
NA PRÁTICA
Os drivers de outros bancos de dados podem ser baixados normalmente
nos sites dos respectivos fabricantes. Em alguns casos, como no Microsoft
SQL Server, existem grupos alternativos que desenvolvem o driver em
http://jtds.sourceforge.net. O driver do MySQL (chamado de MySQL Connector)
pode ser baixado no site http://www.mysql.org.
Ampliando o foco
http://www.hibernate.org
http://jtds.sourceforge.net
http://www.mysql.org
34
public static Connection acessoMySQL() {
Connection con = null; // definição do objeto de conexão com o banco
try {
// definição do driver de conexão para o MySQL
String driver = "com.mysql.jdbc.Driver";
// registro da classe de acesso ao banco não é necessário a partir
// do JDBC 4, presente na versão Java 6, versões anteriores poderão
// exigir esse registro
Class.forName(driver);
// determinar o caminho de acesso ao servidor
// para acesso local, ou seja, a própria máquina, você pode usar
// localhost ou 127.0.0.1
// ou você pode indicar o endereço IP do servidor
String nomeServidor = "localhost";
// nome do banco de dados que será aberto
// um servidor pode ter mais de um banco de dados registrado
String nomeBanco = "BD_teste";
// definição da configuração de acesso ao servidor
String url = "jdbc:mysql://" + nomeServidor + "/" + nomeBanco;
// executa a conexão com o banco de dados
// caso não consiga realizar a conexão, uma exceção será lançada
con = DriverManager.getConnection(url);
// testa se sua conexão foi realizada//
if (con != null) {
System.out.println("Banco conectado com sucesso!");
} else {
System.out.println("Banco não conectado!");
}
}
// caso seja lançada uma exceção de driver não encontrado,
// provavelmente a biblioteca de acesso do driver
// não foi incluída no projeto
catch (Exception e) {
e.printStackTrace();
}
return con;
}
• Driver: com.mysql.jdbc.Driver.
• Exemplo de método de conexão.
35
Resumo da Unidade 1
Nesta unidade foram abordados conceitos de diferentes modelos de arquitetura de sis-
temas computacionais (modelo em camadas, cliente-servidor e multicamadas), além de
alguns padrões de projeto (MVC, DAO, Factory e Singleton) para desenvolvimento de sis-
temas OO com acesso a bancos de dados e aplicações Web. Também foram apresenta-
das técnicas de acesso e conexão a sistemas gerenciadores de banco de dados – SGBD
com uso da biblioteca JDBC.
36
Referências
DEITEL, P.; DEITEL, H. Java: como programar. 10. ed. São Paulo: Pearson Education do
Brasil, 2017. Cap. 24, p. 13-860. Biblioteca Virtual.
FOWLER, M. Analysis Patterns: Reusable Object Models. Londres: Addison-Wesley
Reading, 1996.
FREEMAN, E.; FREEMAN, E.; SIERRA, K.; BATES, B. Use a cabeça: padrões e projetos. 2.
ed. rev. Rio de Janeiro: Alta Books, 2009. xxiv, 478 p. ISBN 9788576081746.
FURGERI, S. Java 8 – Ensino didático: desenvolvimento e implementação de aplicações.
São Paulo: Érica, 2015. Cap. 12, p. 232-256. Minha Biblioteca
GAMMA, E. et al. Padrões de projeto: soluções reutilizáveis de software orientado a ob-
jetos. Porto Alegre: Bookman, 2000. xii, 364 p. ISBN 9788573076103.
LARMAN, C. Utilizando UML e padrões: uma introdução à análise e ao projeto orienta-
dos a objetos e ao desenvolvimento iterativo. Porto Alegre: Bookman, 2000. 492 p.
Acesso a bancos de dados
relacionais via JDBC utilizando
diferentes padrões de projeto
UNIDADE 2
38
Nesta unidade serão abordadas técnicas para desenvolver aplicações Java com persis-
tência de dados com uso da biblioteca JDBC por meio de mapeamento objeto-relacio-
nal. O aluno terá oportunidade de aprender com exemplos de acesso a banco de dados
utilizando os modelos de projeto DAO e “Factory” para implementar operações CRUD e
mapeamento objeto-relacional não automático.
Como estamos tratando de sistemas computacionais multiusuários com acessos con-
correntes ao mesmo banco de dados, é necessário que o SGBD implemente algum tipo
de controle de acesso para que cada transação seja atendida de forma justa e eficiente.
Assim, serão abordadas diferentes técnicas de controle concorrente de transações em
banco de dados. Caso haja falha em uma transação, seus efeitos deverão ser revertidos
para garantir a integridade e a consistência do banco de dados.
Também serão abordadas recomendações para o tratamento adequado de erros e exce-
ções em sistemas Java com acesso a banco de dados.
INTRODUÇÃO
Nesta unidade você será capaz de:
• Desenvolver aplicações em Java com persistência de dados por meio de
mapeamento objeto-relacional manual.
OBJETIVO
39
Acesso remoto a gerenciadores de bancos
de dados relacionais com usodo JDBC
Neste tópico serão abordados alguns exemplos práticos de programação Java para uti-
lização das classes da biblioteca JDBC para conexão e implementação das operações
CRUD em banco de dados.
Utilizaremos como SGBD o MySQL Server, que é mantido pela Oracle e amplamente uti-
lizado em aplicações comerciais. Em seguida, apresentaremos o modelo de conexão ao
MySQL usando JDBC API e Driver Manager, como mostra a figura a seguir.
Figura: Modelo de conexão ao MySQL usando JDBC API e Driver Manager.
Fonte: Notas de aula do professor (2020).
Fábrica de Conexões (“Connection Factory”)
Como já vimos muitas vezes, o processo de criação de um objeto pode exigir critérios
específicos que não são adequados para incluir dentro de seu próprio método construtor.
No caso específico de criação de uma conexão ao banco de dados, são necessárias in-
MySQL
Java Application
JDBC API
JDBC Driver
Manager
JDBC Driver
40
formações específicas dos SGBD, diferentes strings de conexão, o que pode gerar maior
acoplamento e menor coesão na classe que está sendo desenvolvida.
Dessa forma, o padrão “Factory Method” pode ser usado para definir uma interface para
a criação das conexões do banco de dados a fim de controlar melhor esse processo
repetitivo e trabalhoso, que consome muitos recursos do SGBD.
Veja um exemplo de classe responsável por criar uma nova conexão com o banco de dados.
No exemplo anterior, o tratamento das exceções não está sendo realizado de forma ade-
quada, pois a SQLException é uma “exception checked”, que pode ser lançada por muitos
dos métodos da API de JDBC. É necessário fazer um tratamento de exceções específico,
usando a estrutura “try/catch”, quando há possibilidade de recuperação do sistema caso
ocorra alguma falha.
Dessa forma, para obter uma nova conexão com o banco de dados basta usar o seguinte
comando:
Para criar uma conexão JDBC, é necessário usar a classe DriverManager pre-
sente no pacote java.sql. A string de conexão contendo o endereço URL do
banco de dados, o usuário e a senha deve ser repassada ao método estáti-
co getConnection() da classe DriverManager para que ela possa criar uma
conexão JDBC:
Exemplo
public class ConnectionFactory {
public Connection criaConexao() throws SQLException{
return
DriverManager.getConnection("jdbc:mysql://localhost/BD_teste,"ro
ot","senha");
}
}
Connection conexao = ConnectionFactory.criaConexao();
41
O método “criaConexao()” pode ser considerado uma fábrica de conexões, pois, ao ser
executado, retorna um objeto “Connection” representando a criação de uma nova cone-
xão pronta para uso, independentemente de detalhes da codificação.
Apagando uma base de dados
Depois de implementar uma conexão JDBC, podemos começar a executar opera-
ções no banco de dados. O primeiro exemplo de operação será apagar uma base de
dados “bdteste”. Inicialmente, para executar uma operação deve-se definir o código
SQL correspondente.
O código SQL que corresponde à operação a ser executada deve ser usado como parâ-
metro para a interface “prepareStatement()” de uma conexão JDBC. Essa interface cria
um objeto do tipo “PreparedStatement”, que representa a operação que será executada.
Em seguida, a operação SQL poderá ser realizada por meio do método “execute()”. No
final, deve-se chamar o método “close()” do objeto “PrepareStatment” para liberação de
recursos de memória.
String sql = "DROP DATABASE IF EXISTS bdteste";
PreparedStatement stm = conexao.prepareStatement(sql);
stm.execute();
stm.close();
Uma mesma conexão pode ser reaproveitada para executar outras operações
do banco de dados. A conexão JDBC deverá ser finalizada a partir do método
“close()” quando não houver mais operações a serem executadas, a fim de libe-
rar recursos no SGBD:
Importante
conexao.close();
42
Criando uma base de dados
O procedimento de criação de uma base de dados é similar ao procedimento anterior
para apagar uma base de dados:
Criando uma tabela de dados
O código a seguir mostra o procedimento de criação de uma tabela a partir de uma
conexão JDBC.
JavaBeans
JavaBeans são classes que possuem o construtor “default” sem argumentos e apenas
os métodos básicos de acesso “get” e “set”, cuja especificação é a base dos componen-
tes escritos em Java.
Neste tópico usaremos o conceito JavaBeans nas classes que representam nosso
modelo de dados.
String sql = "CREATE DATABASE dbpoo3"
PreparedStatement stm = conexao.prepareStatement(sql);
stm.execute();
stm.close();
String sql = "CREATE TABLE alunos (" + ;
" idaluno INT NOT NULL AUTO_INCREMENT ," +
" matricula INT NOT NULL" +
" nome VARCHAR(45) NOT NULL ," +
" PRIMARY KEY (idaluno))" ;
PreparedStatement stm = conexao.prepareStatement(sql);
stm.execute();
stm.close();
43
Veja um exemplo de uma classe “Aluno” no padrão “JavaBeans”, que seria equivalente ao
nosso modelo de entidade do banco de dados da tabela “Alunos”:
Exemplo
public class Aluno {
private int idaluno;
private int matricula;
private String nome;
// métodos get e set da classe Aluno
public String getNome() {
return this.nome;
}
public void setNome(String novo_nome) {
this.nome = novo_nome;
}
public int getId() {
return this.idaluno;
}
public void setId(int novo_id) {
this.idaluno = novo_id;
}
public int getMatricula() {
return this.matricula;
}
public void setMatricula(int novo_mat) {
this.matricula = novo_mat;
}
}
A especificação JavaBeans é a base dos componentes escritos em Java e sua
documentação completa pode ser encontrada em http://docs.oracle.com/java-
se/tutorial/javabeans/.
Ampliando o foco
http://docs.oracle.com/javase/tutorial/javabeans/
http://docs.oracle.com/javase/tutorial/javabeans/
44
Inserindo dados em uma tabela
Agora, veremos um exemplo de como adicionar dados nas tabelas utilizando conexões
JDBC.
Normalmente, as chaves primárias dos registros inseridos em uma tabela são geradas
pelos SGBDs. Caso necessário os valores da chave primária podem ser obtidos a partir
dos métodos definidos pela especificação JDBC.
O exemplo anterior possui alguns problemas. São eles:
• A mistura das sintaxes das linguagens Java e SQL torna o código confuso e
de difícil entendimento, especialmente para operações de inserção e atualização
de dados.
• A necessidade de concatenação de strings longas para implementar os
comandos SQL, principalmente com tabelas de dados grandes com várias colunas.
• O tratamento de caracteres especiais nas strings e o risco de alteração do código
SQL por usuários mal-intencionados (SQL Injection).
Não confunda JavaBeans com outro componente chamado de Enterprise
JavaBeans – EJB. Os EJBs são componentes da plataforma Java Enterprise
Edition – JEE, que roda em um “container” de um servidor de aplicação. Seu
objetivo é fornecer um desenvolvimento de aplicações Java de forma mais
simples e rápida, baseado em componentes distribuídos, transacionais, segu-
ros e com portabilidade.
Importante
String sql = "INSERT INTO alunos (matricula , nome)” +
“VALUES ( ’202001’,’João da Silva’)";
PreparedStatement stm = conexao.prepareStatement(sql);
stm.execute();
stm.close();
45
Por esses motivos a forma de entrada do código SQL será atualizada da seguinte maneira:
Nesta sintaxe, o comando SQL será executado, mas não sabemos os parâmetros que
utilizaremos no código SQL. As cláusulas são executadas em um banco de dados por
meio da interface PreparedStatement. Para receber uma PreparedStatement relativa à
conexão, basta chamar o método prepareStatement, passando como argumento o co-
mando SQL com os valores oriundos de variáveis preenchidos com uma interrogação.
Logo, chamamos os métodos setLong e setString do PreparedStatementpara preen-
cher os valores, que são do tipo “int” e “String”, passando a posição (começando em 1) da
interrogação no SQL e o valor que deve ser colocado:
Por fim, uma chamada ao método “execute()”para executar o comando SQL:
String sql = "INSERT INTO alunos (matricula, nome) VALUES (?,?)";
String sql = "INSERT INTO alunos (matricula, nome) VALUES (?,?)";
PreparedStatement stm = conexao.prepareStatement(sql);
// preenche os valores
stm.setInt(1, 202001);
stm.setString(2, "João da Silva");
stm.execute();
46
O exemplo a seguir abre uma conexão e adiciona um aluno ao banco de dados:
public class JDBCadiciona {
public static void main(String[] args) throws SQLException {
// conectando
Connection conexao = ConnectionFactory.criaConexao();
// cria um preparedStatement
String sql = "INSERT INTO alunos (matricula,nome) VALUES (?,?)";
PreparedStatement stm = conexao.prepareStatement(sql);
// preenche os valores
stm.setInt(1, 202001);
stm.setString(2, "João da Silva");
// executa
stmt.execute();
stmt.close();
System.out.println("Gravado!");
conexao.close();
}
Sobre a interface Statement, em vez de se usar o PreparedStatement é possível
utilizar uma interface mais simples chamada Statement, que simplesmente
executa uma cláusula SQL no método “execute”:
O problema é que, apesar de ser mais simples, a interface Statment é mais len-
ta e será necessário implementar muitas concatenações de strings, tornando o
código mais difícil de entender. Dessa forma, é recomendável utilizar apenas a
classe PreparedStatement.
Statement stm = conexao.createStatement();
stm.execute("INSERT INTO ...");
stm.close();
Importante
47
Classe Data Access Object – DAO
A separação do código de acesso ao banco de dados em uma classe DAO, com apenas
essa responsabilidade, aumenta a coesão e diminui o acoplamento do código, facilitan-
do sua manutenção e sua atualização. Da responsabilidade desse objeto surgiu o nome
Data Access Object, ou simplesmente DAO, um dos mais utilizados padrões de projeto
(design patterns).
Podemos implementar a classe “AlunoDAO” com um método construtor, que cria uma
conexão com banco de dados por meio de uma chamada ao método “criaConexao()” da
classe ConnectionFactory.
Então, uma instância de AlunoDAO já possui uma conexão com o banco de dados e
podemos implementar o método “adicionaAluno”, que recebe um objeto “Aluno” como
argumento e é responsável por adicioná-lo a partir de código SQL:
public class AlunoDAO {
// abre uma conexão com o banco de dados
private Connection conexao;
public AlunoDAO() {
this.conexao = ConnectionFactory.criaConexao();
}
}
public void adicionaAluno(Aluno aluno) throws SQLException{
String sql = "INSERT INTO alunos (matricula,nome) VALUES (?,?)";
// comando prepared statement para inserção de dados
PreparedStatement stm = conexao.prepareStatement(sql);
// entra com os valores de inserção
stm.setInt(1,aluno.getMatricula());
stm.setString(2,aluno.getNome());
// executa
stm.execute();
stm.close();
}
48
Fazendo pesquisas no banco de dados
A implementação de pesquisas no banco de dados também utiliza a interface
“preparedStatement” para montar o comando SQL. Entretanto, como uma pesquisa no
banco possui valores de retorno (diferentemente do comando de inserção), será utilizado
o método “executeQuery”, que retorna todos os registros de uma determinada consulta.
O objeto retornado é do tipo “ResultSet” do JDBC, que possibilita navegar por seus re-
gistros por meio do método “next”. Quando chega ao fim da pesquisa, o método “next”
retorna um valor “false” e dessa forma pode ser utilizado para fazer um laço (“loop”) nos
registros. Para retornar o valor de uma coluna no banco de dados, basta chamar os mé-
todos “get” do “ResultSet”, como “getString”, “getInt”, “getLong”, entre outros.
Aplicando o conceito DAO, podemos criar um método “getListaAlunos()” na nossa classe
AlunoDAO retornando uma lista de objetos “Aluno”:
// exemplo de consulta simples
// inicia o prepareStatement
PreparedStatement stm = conexao.prepareStatement("SELECT * FROM alunos");
// executa uma consulta select
ResultSet rs = stm.executeQuery();
// iteração no ResultSet
while (rs.next()) {
int matricula = rs.getInt("matricula");
String nome = rs.getString("nome");
System.out.println(nome + " - " + matricula);
}
rs.close();
stm.close();
conexao.close();
public List<aluno> getListaAlunos() throws SQLException {
String sql = "SELECT * FROM alunos"
PreparedStatement stm = this.conexao.prepareStatement(sql);
ResultSet rs = stm.executeQuery();
List<Aluno> alunos = new ArrayList<Aluno>();
while (rs.next()) {
// criando o objeto Aluno
Aluno aluno = new Aluno();
aluno.setMatricula(rs.getInt("matricula"));
aluno.setNome(rs.getString("nome"));
// adicionando o objeto à lista
alunos.add(aluno);
}
rs.close();
stm.close();
return alunos;
}
49
Métodos para alteração e remoção de registros
Nos métodos que apresentaremos a seguir também vamos utilizar a interface
“preparedStatement” para executar os códigos SQL de alteração (“update”) e remo-
ção (“delete”). O método “ResultSet” será usado para receber os dados retornados de
cada pesquisa.
A seguir é mostrado o método “modificaAluno”, que recebe um objeto do tipo “Aluno”,
cujos valores serão alterados na tabela do banco de dados:
O código seguinte implementa a remoção de um registro de aluno com uma consulta
baseada na chave primária “idaluno” a fim de executar o comando SQL para deleção do
registro na tabela “Alunos”:
public List<aluno> getListaAlunos() throws SQLException {
String sql = "SELECT * FROM alunos"
PreparedStatement stm = this.conexao.prepareStatement(sql);
ResultSet rs = stm.executeQuery();
List<Aluno> alunos = new ArrayList<Aluno>();
while (rs.next()) {
// criando o objeto Aluno
Aluno aluno = new Aluno();
aluno.setMatricula(rs.getInt("matricula"));
aluno.setNome(rs.getString("nome"));
// adicionando o objeto à lista
alunos.add(aluno);
}
rs.close();
stm.close();
return alunos;
}
public void modificaAluno(Aluno aluno) throws SQLException{
String sql = "UPDATE alunos SET matricula=?, nome=? WHERE id=?";
PreparedStatement stm = conexao.prepareStatement(sql);
stm.setInt(1, aluno.getMatricula());
stm.setString(2, aluno.getNome());
stm.setInt(3, aluno.getIdaluno());
stm.execute();
stm.close();
}
public void removeAluno(Aluno aluno) throws SQLException{
String sql = "DELETE FROM alunos WHERE idaluno=?";
PreparedStatement stm = conexao.prepareStatement(sql);
stm.setLong(1, aluno.getIdaluno());
stm.execute();
stm.close();
}
50
Técnicas de acesso concorrente a bancos
de dados cliente-servidor
Quando o acesso a um determinado item do banco de dados é realizado por apenas um
usuário de cada vez, o funcionamento do Sistema Gerenciador do Banco de Dados é
bem simples, sem a necessidade de métodos complexos de controle de acesso.
O problema começa quando existem acessos concorrentes a um mesmo item do banco
de dados por mais de um usuário e de forma simultânea. Nestes casos, é necessário que
o SGBD implemente algum tipo de controle de acesso para que cada transação concor-
rente seja atendida de forma justa e eficiente.
Para a implementação de um sistema computacional multiusuárioscom acesso concor-
rente às informações do banco de dados é necessário que o SGBD controle a execução
das transações de cada usuário. Se houver algum problema ou falha em uma transação,
seus efeitos deverão ser revertidos (“rollout”) para manter a integridade e a consistência
do banco de dados.
O que é uma transação em um banco de dados?
É qualquer conjunto de operações de acesso a uma base de dados, formando uma uni-
dade lógica de processamento. Pode incluir uma ou mais operações de acesso, criação,
consulta, atualização ou remoção de itens em uma base de dados.
Transações concorrentes em bancos de dados
Quando diferentes usuários tentam acessar um mesmo item de base de dados de forma
concorrente é necessário que o sistema computacional realize um controle de acesso
entre essas transações.
O Sistema Gerenciador de Banco de Dados – SGBD deve garantir que todas as operações
completadas com sucesso em uma transação sejam gravadas de forma permanente no
banco de dados. Por outro lado, se houver alguma falha durante a execução de uma
transação, ela não deverá alterar o banco de dados ou outras transações concorrentes.
Existem várias técnicas de controle de concorrência para garantir que não haja interfe-
rência e se mantenha o isolamento entre as transações concorrentes na base de dados.
51
Algumas dessas técnicas, como controle por escalonamento e controle por bloqueio,
serão mostradas neste tópico.
A finalização de uma transação pode ocorrer de duas formas:
• Committ Transaction: a transação terminou com sucesso e será gravada de for-
ma permanente na base de dados.
• Rollback Transaction: houve erro durante a transação e a base de dados deve
retornar ao estado anterior à transação.
Dessa forma, para evitar falhas catastróficas na base de dados com perda de informa-
ções é necessário desenvolver planos de contingência. Conheça alguns:
• Verificação de falhas durante e após as transações.
• Procedimentos de restauração após as falhas (“abort”).
• Retorno do histórico de transações (“rollback ”).
• Realização de backups de acordo com estratégias de armazenamento (periodici-
dade de backups totais, incrementais etc.).
• Planos de recuperação e verificação de backups.
Veja alguns exemplos de falhas que podem acontecer durante uma transação.
• Falha durante a execução da transação.
• Condições de exceção detectadas pela transação.
• Problemas de infraestrutura: queda de energia, rede, falhas de componentes
de hardware.
• Erros humanos, sabotagem.
Exemplo
52
Figura: Estados de uma transação em banco de dados.
Fonte: Notas de aula do professor (2020).
Propriedades de uma transação
Para conseguir gerenciar as transações de forma adequada é necessário entender suas
propriedades conhecidas pelo acrônimo Acid (atomicidade, consistência, isolamento e
durabilidade). Os métodos de controle de concorrência das transações e recuperação do
SGBD devem utilizar essas propriedades para manter a integridade dos dados.
Vamos conhecer com mais detalhes cada uma delas.
1. Atomicidade
O conceito de atomicidade considera cada transação como uma unidade de proces-
samento individual, sem separações. A transação somente será considerada realizada
quando todas as operações da unidade lógica de processamento forem executadas sem
erros. Caso contrário, toda a transação deverá ser cancelada, retornando ao estado ante-
rior dos dados (“rollback”) para garantir a consistência e a integridade do banco. Apenas
no caso de todas as operações serem executadas de forma adequada é que a transação
será persistida no banco de dados (“commit”).
2. Consistência
A propriedade consistência considera que uma transação somente poderá ser efetivada
no banco de dados quando todas as suas regras, condições e restrições predefinidas
forem atendidas. Por exemplo, devem ser atendidas as regras de relacionamento por
chaves estrangeiras e verificação de valores permitidos para campos restritos. Essa pro-
priedade garante a execução de uma transação sem a interferência de outras, mantendo
a consistência do banco de dados.
Transação ativa
em execução
read, write,...
begin end
abort
abort
commit
rollback
Efetivação
parcial
Transação
efetivada
Operação
abortada
Falha
53
3. Isolamento
Devido à propriedade de isolamento, mesmo que existam transações concorrentes e si-
multâneas, cada uma delas deverá funcionar de modo independente. Durante a execução
de uma transação, nenhuma outra transação concorrente poderá interferir no funciona-
mento da primeira. Essa propriedade também garante que os resultados parciais de uma
transação em execução não possam ser acessados por outras transações concorrentes.
4. Durabilidade
A propriedade durabilidade garante que somente uma nova transação pode alterar os
resultados de uma transação anterior que foram armazenados de forma permanente no
banco de dados. Mesmo em caso de alguma falha no sistema, todas as operações de
uma transação finalizada devem ser armazenadas no banco de dados.
Figura: Propriedades de uma Transação em banco de dados.
Fonte: Notas de aula do professor (2020).
É importante notar que o SGBD geralmente armazena um registro das transações exe-
cutadas pelo usuário (arquivo de “log”) para que essas ações possam ser desfeitas caso
ocorra alguma falha. Esse arquivo de registros também pode ser usado para garantir a
durabilidade. Assim, no caso de falhas no sistema antes da execução de alguma transa-
ção, o arquivo de “log” pode ser usado para restaurar (“rollback”) o estado do banco de
dados quando o sistema for reiniciado.
Propriedades
de uma transação
ATOMICIDADE
CONSISTÊNCIA
ISOLAMENTO
DURABILIDADE
54
Técnicas de controle de concorrência
É comum que existam acessos simultâneos a um mesmo banco de dados em siste-
mas multiusuários. Dessa forma, é necessário que os SGBDs implementem técnicas de
controle de concorrência entre as transações no banco de dados que estiverem sendo
executadas de forma simultânea. O controle de concorrência é necessário para manter
a integridade e a consistência das informações e pode ser utilizado para garantir as pro-
priedades Acid de uma transação.
As técnicas de controle de concorrência precisam detalhar todas as operações execu-
tadas no banco de dados entre o início e o fim de cada transação. Para que transações
concorrentes mantenham a consistência e a integridade dos dados, é necessário ga-
rantir que a sequência de operações dessas transações tenha o mesmo resultado de
outra transação qualquer que foi executada sem nenhuma concorrência.
Este é o conceito da serialização, quando a ordem de execução das operações das tran-
sações concorrentes deve ser equivalente a uma transação sequencial sem concorrên-
cia. A serialização visa garantir que as transações concorrentes sejam executadas de
forma adequada e que o estado final do banco de dados mantenha sua consistência.
Controle por escalonamento (“scheduler”)
Este método de controle de concorrência utiliza um escalonador (“Scheduler”), que visa
ordenar de forma sequencial as ações que seriam executadas por uma ou mais transa-
ções em um banco dados.
• Escalonamento serial: as transações concorrentes são executadas no banco de
dados de forma sequencial, isto é, uma após a outra.
• Escalonamento não serial: transações executadas de forma simultânea.
- Escalonamento serializável: as ações de transações concorrentes simultâ-
neas podem ser executadas de forma serial ou sequencial, atingindo o mesmo
resultado final.
- Escalonamento não serializável: o resultado da execução das transações de
forma concorrente é diferente da execução serial. Neste caso, não há garantia de
que o estado final do banco de dados seja consistente.
55
Figura: Tipos de Escalonamento.
Fonte: Notas de aula do professor (2020).
Controle por bloqueios
A implementação de bloqueios sobre elementos do banco de dados é uma técnica para
evitar comportamento não serializável das transações. As técnicas de bloqueio para
controle de concorrênciasão baseadas em mecanismos que permitem a uma transação
impedir que outras acessem ou atualizem registros do banco de dados. Dessa forma, é
possível evitar problemas de concorrência e inconsistência nos dados.
O bloqueio pode ser implementado por meio de uma variável que fica atrelada ao item de
dados envolvido na transação que está sendo realizada. Esse método de bloqueio pode
ser implementado de forma binária (“booleana”) com dois estados possíveis determina-
dos pela variável:
• Estado “bloqueado” com valor “1” ou “True”.
• Estado “desbloqueado” com valor “0” ou “False”.
Assim, se o item do banco de dados for acessado por alguma transação, o item estará
bloqueado, pois a variável possuirá valor “1” (“True”). Caso contrário, se o item estiver
desbloqueado, terá valor “0” ou “False”.
Normalmente, são usadas as operações “lock” e “unlock” para o bloqueio binário. A ope-
ração “lock” configura a variável de bloqueio (“True”) quando o item de dados está sendo
acessado por alguma transação. Logo que a transação encerra a utilização do item, é lan-
çada a operação “unlock” para “resetar” a variável (“False”), e o item já estará desbloqueado.
Serializável Não Serializável
Serial Não Serial
Escalonamento
56
Existem duas formas de bloquear os dados:
• Bloqueio Compartilhado: nesse tipo de bloqueio, somente quando a transação
possui operações de apenas leitura, outras transações concorrentes podem aces-
sar o mesmo dado. Se a transação possuir operações de gravação, não poderá
realizar bloqueio compartilhado.
• Bloqueio Exclusivo: para esse tipo de bloqueio, o item do banco fica reservado
para a operação que compõe a transação. Assim, outras transações concorrentes
não poderão acessar o item do banco de dados que está sendo utilizado. Geral-
mente, se um item do banco está sofrendo uma operação de gravação, a transação
corrente deve receber um bloqueio exclusivo para evitar que outras transações si-
multâneas causem falhas ou interferências no item.
É comum que uma transação mantenha o bloqueio ao item durante todo o tempo em
que estiver realizando o acesso ao banco de dados. Em alguns casos, o desbloqueio
imediato após terminar um acesso ao item do banco não é recomendado, uma vez que
pode prejudicar o processo de serialização das transações concorrentes.
Bloqueio em duas fases
Esse tipo de bloqueio pode ser usado para manter um escalonamento de forma serial.
Para isso, antes de poder liberar qualquer bloqueio, cada transação deve primeiro realizar
todos os seus bloqueios. Dessa forma, uma transação não pode liberar o bloqueio de um
item e em seguida realizar bloqueio de outro.
O bloqueio em duas fases é composto pelas seguintes fases:
• Fase de crescimento, em que todos os bloqueios necessários são realizados.
• Fase de encolhimento, em que há a liberação dos bloqueios.
Existe uma variante dessa técnica de bloqueio em duas fases denominada “Strict”, em
que a fase de encolhimento é iniciada apenas quando toda a transação termina. A van-
tagem da técnica “Strict” é que uma transação sempre lê valores escritos por uma outra
transação já executada e finalizada. Por exemplo, se houve necessidade de recuperação
de dados (“rollback”) durante uma transação, não haverá inconsistência de dados propa-
gando-se para uma transação concorrente.
57
Problemas de concorrência entre transações
Caso as propriedades Acid não sejam respeitadas pelos SGBDs, podem acontecer al-
guns problemas durante a execução de transações concorrentes. Vejamos alguns deles:
• Atualização perdida (“lost update”)
Esse problema pode ocorrer quando duas transações concorrentes T1 e T2 leem os
mesmos dados do banco e tentam atualizar os dados com base no que foi lido antes que
uma das atualizações seja realizada com sucesso.
Figura: Exemplo de atualização perdida.
Tempo T1 T2 Descrição
t0 Início: registro A = 10
t1 read (A,x) leitura do registro A na variável x
t2 read (A, y) leitura do registro A na variável y
t3 x = x + 1 x = 11
t4 y = y + 5 y = 15
t5 write (A, x) escrita da variável x = 11 no registro A
t6 write (A, y) escrita da variável y = 15 no registro A
Resultado: A = 15 (atualização da transação T1 é perdida devido à T2).
Fonte: Notas de aula do professor (2020).
• Leitura suja (“dirty read”)
Ocorre quando uma transação está tentando atualizar um item do banco de dados e ou-
tra transação concorrente lê esse item que ainda não foi atualizado.
58
Figura: Exemplo de leitura suja.
Tempo T1 T2 Descrição
t0 Início: registro A = 10
t1 read (A, x) leitura do registro A na variável x
t2 x = x + 1 x = 10 + 1 = 11
t3 write (A, x) escrita da variável x = 11 no registro A
t4 read (A, y) leitura do registro A = 11 na variável y
t5 rollback T1 ocorreu uma falha. desfaz T1 e retoma a = 10
t6 y = y + 5 y = 11 + 5 = 16
t7 write (A, y) escrita da variável y no registro A
Resultado: A = 16 (T2 fez uma leitura “suja” do registro A porque depois foi alterado).
Fonte: Notas de aula do professor (2020).
• Leitura fantasma (“ghost read”)
Uma transação está realizando uma segunda consulta consecutiva ao banco, que re-
torna um resultado que atende a uma certa condição de procura. O problema é que o
segundo resultado é diferente da primeira consulta, pois no intervalo entre as consultas
houve a execução de uma transação concorrente.
Figura: Exemplo de leitura fantasma.
Tempo T1 T2 Descrição
t0 Início: tabela A com 2 registros (a, b)
t1 select * from A Retorna 2 registros
t2 insert intoA valeus (“c”) tabela A com 3 registros (a, b, c)
t3 select * from A Retorna 3 registros
Resultado: Leituras repetidas de T1 na tabela A retornam diferentes registros.
Fonte: Notas de aula do professor (2020).
59
Problemas com as técnicas de bloqueio
Sempre haverá necessidade de bloqueio e desbloqueio dos itens de dados com acesso
compartilhado, mas existem algumas situações em que a combinação desses estados
pode gerar problemas no banco dados. Veremos alguns desses problemas a seguir:
• Impasse (“deadlock”)
O impasse (“deadlock”) pode ocorrer se não for realizado o desbloqueio do item do banco
antes da solicitação de um bloqueio a outro item. Por exemplo, existem duas transações
concorrentes T1 e T2. A primeira transação, T1, está esperando por um item de dados
que está bloqueado pela transação T2. Simultaneamente, a transação T2 está esperando
por outro item, que está bloqueado por T1. Neste caso, as duas transações ficam conge-
ladas, esperando indefinidamente que algum dos itens bloqueados seja liberado. Como
isso não ocorre, as duas transações não conseguem ser concluídas.
Existem algumas formas de tratar o problema de impasse. Por exemplo, alguns sistemas
conseguem detectar um “deadlock” e podem escolher uma transação do conjunto para
abortar a operação (“rollback”), eliminando o ciclo de espera. O problema de “deadlock”
pode ser resolvido pela técnica de controle por rótulo de tempo, que será vista a seguir.
Figura: Deadlock.
Transação T1 Transação T2
Tabela A Tabela B
Fonte: Notas de aula do professor (2020).
T1 quer acesso em B mas
T2 está bloqueando B
T2 quer acesso em A mas
T1 está bloqueando A
Deadlock
60
• Estagnação (“starvation”)
O problema de estagnação pode ocorrer quando uma transação de alteração a um de-
terminado item não consegue ser executada devido a outras transações concorrentes de
leitura ao mesmo item do banco de dados.
Por exemplo, uma transação de leitura de um item está sendo executada e faz uma re-
quisição de bloqueio compartilhado. Se uma transação concorrente de alteração tentar
acesso ao mesmo item não conseguirá obter a requisição de bloqueio exclusivo e ficará
aguardando o término da primeira transação.
Se outras transações concorrentes de leitura ao mesmo item do banco chegarem ao
SGBD e não houver um escalonador com controle de tempo, a transação de alteração
nunca será executada, pois não conseguirá implementar o bloqueio exclusivo. Como as
transações de leitura mantêm o bloqueio compartilhado do item,a transação de altera-
ção fica em “estagnação”, pois não consegue obter o acesso e fica esperando a liberação
indefinidamente.
Controle por rótulo de tempo (“TimeStamp”)
Nesse tipo de controle de concorrência, para cada transação iniciada é associado
um rótulo de tempo (“TimeStamp”) fixo e exclusivo. Assim, antes que uma transação
inicie sua execução, o SGBD fornecerá um rótulo de tempo exclusivo para identificar
essa transação.
Por exemplo, temos duas transações, T1 e T2; a transação T1 iniciou-se no tempo “n”, e
a transação T2 teve início no tempo “n+1”. Logo, a transação T1 será executada primeiro
do que a transação T2, pois seu tempo de início é mais antigo.
Existem duas formas para implementação desse controle, usando como rótulo de tempo:
• Hora do relógio do sistema computacional: neste caso, o horário de início da
transação será igual à hora em que a transação entrar no sistema.
• Contador lógico incrementado sempre que houver um novo “TimeStamp”: o
rótulo de tempo da transação é igual ao valor do contador quando a transação entra
no sistema.
61
Esse controle precisa garantir que a ordem em que o item do banco de dados está sendo
acessado não viola a ordem do “TimeStamp”. Dessa forma, o controle associa a cada
item “X” do banco de dados dois valores de rótulo de tempo (“TimeStamp” — TS):
• read_TS(X) - “TimeStamp” de leitura do item “X”.
• write_TS(X) - “TimeStamp” de gravação do item “X”.
Esses rótulos de tempo devem ser atualizados sempre que uma nova instrução de leitura
ou escrita é executada. A todo momento em que uma transação de leitura ou escrita é
desfeita pelo esquema de controle de concorrência, essa transação é reiniciada com um
novo “TimeStamp”.
Tal controle de concorrência garante que, se houver operações de leitura ou escrita em
conflito, essas operações serão executadas por ordem de “TimeStamp”.
O controle de “TimeStamp” pode prevenir o problema de impasse (“deadlock”) visto an-
teriormente. Caso duas transações estejam envolvidas em um “deadlock”, a transação
mais nova será abortada pela transação mais antiga (a que entrou primeiro).
Nesse tipo de controle também existe a possibilidade de paralisação de transações lon-
gas, caso uma série de transações curtas causem o reinício da transação longa. Nesse
caso, as transações curtas podem ser suspensas temporariamente para permitir que a
transação longa seja concluída.
Como é possível ver, existem várias técnicas que os SGBDs devem implementar para
evitar os problemas de acesso concorrente e manter a integridade dos bancos de da-
dos multiusuários.
62
Tratamento de erros em bancos de dados
Neste tópico abordaremos as diferentes formas de tratamento de erros em aplicativos
de bancos de dados e também como tratar, em Java, algumas situações. Conheça
algumas delas:
• Divisão por zero.
• Erro na conversão de tipos (ex.: converter uma string de letras em um número inteiro).
• Erro na abertura ou na transmissão de um arquivo.
• Erro de impressão.
• Acesso a um vetor com índice inválido.
• Erro de conexão a um banco de dados, entre outros.
Todas as situações listadas acima em Java são chamadas de exceções e existe um me-
canismo específico para tratá-las, que chamamos de tratamento de exceções.
As exceções em Java estão organizadas em uma hierarquia de classes, como mostra a
figura a seguir.
Figura: Hierarquia de classes em Java.
Fonte: Notas de aula do professor (2020).
63
Um “Error” pode ocorrer devido a problemas no sistema operacional, na Java Virtual
Machine – JVM ou mesmo no hardware. Nesse caso, o melhor a fazer é deixar a JVM
encerrar o programa.
A classe “Exception” é a classe-mãe de todas as exceções que nossos programas po-
dem tratar.
Ela está subdividida em dois ramos:
RuntimeException
Ocorrem devido a um erro de programação, como divisão por
zero, índice inválido do vetor, acesso a objeto nulo etc. Também
são chamadas de exceções não verificadas (unchecked).
Demais exceções
Ocorrem devido a um erro no programa causado por fatores
externos, como erro na abertura de um arquivo, erro na impres-
são, erro na conexão a um banco de dados etc. Também são
chamadas de exceções verificadas (checked).
Quando executamos o programa abaixo:
Podemos observar a seguinte mensagem:
public class DividePorZero {
public static void main(String args[]) {
System.out.println(3/0);
System.out.println("imprime");
}
}
Exception in thread "main" java.lang.ArithmeticException: / by zero
at DividePorZero.main(DividePorZero.java:3)
64
Como o nosso programa não está tratando dessa exceção (divisão por zero), o tratador
padrão do Java executa as seguintes tarefas:
• Imprime o nome da exceção e a mensagem de erro.
• Imprime a pilha de execução (sequência de chamadas dos métodos).
• Termina o programa.
O tratamento de exceções é um mecanismo que permite que o programa defina como
as situações inesperadas serão tratadas.
Existem três comandos relacionados ao tratamento de exceções. São eles:
1. Blocos try...catch...finally.
2. Comando throws.
3. Comando throw.
E alguns métodos que a classe Exception pode implementar:
Exception(String
mensagemErro) getMessage() printStackTrace()
Construtor que permite
criar uma exceção e
armazenar nesse objeto
uma mensagem de erro.
Retorna a mensagem de
erro armazenada
na exceção.
Imprime a pilha de execu-
ção no mesmo formato
da JVM.
É possível tratar várias exceções associando vários catchs ao mesmo try. Nesse caso, a
ordem dos tratadores é importante: eles devem estar ordenados das subclasses para
a superclasse.
try {
// Código a ser tratado
} catch (ArithmeticException e3){ //Primeiro o catch da exceção mais específica.
System.out.printf("Erro de aritmetica: %s\n",e3.getMessage());
} catch(IOException e2) {
System.out.printf("Erro de E/S: %s\n", e2.getMessage());
} catch(Exception e1) { //Por último o catch da exceção mais geral.
System.out.printf("Erro desconhecido: %s\n", e1.getMessage());
}
65
Quando ocorre uma exceção o método cria um objeto do tipo “Exception” e o “dispara”
(throw) para a JVM. O objeto “Exception” criado contém todas as informações sobre o
erro: seu tipo, o local onde ocorreu, uma mensagem de descrição, a pilha de chamadas,
entre outros.
Em seguida a JVM procura um bloco “try...catch” para tratar a exceção no método que
a gerou.
• Encontrou: desvia a execução para o bloco “catch”.
• Não encontrou: procura outro bloco “try...catch” para tratar a exceção na pilha de
execução, ou seja, nos métodos que chamaram o método que gerou a exceção.
- Encontrou: desvia a execução para o primeiro “catch” que encontrar.
- Não encontrou: nenhum tratador na pilha de execução, desvia para o tratador
padrão da JVM, que interrompe a execução do programa.
O tratamento de exceções também pode ser implementado por um bloco
“try…catch...finally”. O bloco “finally” indica um trecho de código que sempre será execu-
tado se uma exceção ocorrer ou não. O bloco “finally” é muito utilizado quando é neces-
sário liberar algum recurso importante do sistema, como uma conexão com o banco de
dados, um arquivo de dados ou memória. Em seguida, veremos exemplos de código com
uso do bloco “finally” para este objetivo.
Figura: Bloco “try … catch … finally”.
Fonte: Notas de aula do professor (2020).
66
As exceções do Java são classificadas como “checked” ou “unchecked”. Para as exce-
ções “checked” (verificadas), o Java nos obriga a usar uma das soluções a seguir.
1. Tratar as exceções no método em que elas podem ocorrer, implementando o blo-
co “try...catch” visto anteriormente.
2. Utilizar o comando “throws” para avisar que estamos cientes de que aquela exce-
ção pode ocorrer, mas não desejamos tratá-la.
3. Utilizar o comando “throw” para disparar uma exceção customizada, ou seja, que
nós mesmos criamos.
- Neste caso, criamos um objeto da classe “Exception” ou de uma de suas subclas-
ses com o operador“new” e o construtor “Exception (String mensagemErro)”, que
permite criar uma exceção e armazenar nesse objeto uma mensagem de erro.
- Em seguida, disparamos a exceção com o comando “throw” e, se necessário,
declaramos que o método irá disparar a exceção com o comando “throws“.
Tratamento de exceções em banco de dados
No primeiro tópico desta unidade criamos uma nova conexão com o banco de dados
sem o tratamento de erros adequado, pois, apesar de a SQLException ser uma “exception
checked” e que pode ser lançada por muitos dos métodos da API de JDBC, foi usado
apenas o comando “throws” para avisar ao compilador que a exceção não seria tratada
e permitir a compilação do código:
Como a SQLException é uma “exception checked” é necessário fazer um tratamento de
exceções mais específico em que haja possibilidade de recuperação de alguma falha.
public class ConnectionFactory {
public Connection criaConexao() throws SQLException{
return DriverManager.getConnection("jdbc:mysql://localhost/BD_teste,"root","root");
}
}
67
Segue um exemplo de alteração do código anterior usando a estrutura “try/catch”:
No exemplo anterior fizemos um bloco “try/catch” para tratamento da SQLException e
depois relançando-a (“throw’) como uma RuntimeException. O objetivo dessa alteração é
para que o código que chamará a classe “ConnectionFactory” (fábrica de conexões) não
fique acoplado à API de JDBC.
Dessa forma, toda vez que tivermos que tratar uma SQLException, poderemos relançá-la
como RuntimeException, ou, então, podemos criar uma exceção customizada para indi-
car que ocorreu um erro dentro da nossa “ConnectionFactory”, como uma nova exceção
“ConnectionFactoryException”.
Fechando uma conexão com o banco de dados
Ao utilizar a biblioteca JDBC deve-se ter cautela no momento de fechar a conexão com o
banco de dados para liberar recursos importantes do sistema. No Tópico 1 desta unidade
foi mostrado o seguinte exemplo de abertura de uma conexão, que insere um aluno no
banco de dados:
public class JDBCInsere {
public static void main(String[] args) throws SQLException {
Connection conexao = ConnectionFactory.criaConexao();
String sql = "INSERT INTO alunos (matricula,nome) VALUES (?,?)";
PreparedStatement stm = conexao.prepareStatement(sql);
stm.setString(1, 2020001);
stm.setString(2, "João da Silva");
stmt.execute();
stmt.close();
System.out.println("Gravado!");
conexao.close();
}
public class ConnectionFactory {
public Connection criaConexao() {
try { return DriverManager.getConnection(
"jdbc:mysql://localhost/BD_teste,"root","root");
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
68
Como o exemplo anterior não fazia o tratamento da SQLException e apenas usava o
comando “throws”, agora vamos mostrar uma alteração para incluir um bloco de trata-
mento “try...catch...finally” na abertura de uma conexão, e, caso ocorra algum problema
no momento de inserir um dado no banco de dados, a conexão será fechada a partir
do bloco “finally”:
Mesmo que o código dentro do bloco “try…catch” lance uma “exception”, o comando
“conexao.close()” para fechamento da conexão será executado. Dessa forma, garanti-
mos que não haverá uma conexão aberta sem uso caso ocorra um erro.
O código para tratamento de exceções pode ficar muito mais complexo. Por exemplo, se
houver lançamento de alguma exceção pelo comando “con.close()”, como será tratado?
Na verdade, trabalhar com recursos escassos e valiosos do sistema opera-
cional, como conexões, sessões, threads e arquivos, sempre deve ser muito
bem planejado. Por motivos de eficiência, performance e segurança nas apli-
cações, sempre deve-se ter cautela para liberar recursos não utilizados.
public class JDBCInsere {
public static void main(String[] args) {
Connection conexao = null;
try{
conexao = ConnectionFactory.criaConexao();
String sql = "insert into alunos (matricula,nome) values (?,?)";
PreparedStatement stm = con.prepareStatement(sql);
stm.setString(1, 2020001);
stm.setString(2, "João da Silva");
stmt.execute();
stmt.close();
System.out.println("Gravado!");
} catch(SQLException e){
System.out.println(e);
} finally {
conexao.close();
}
}
}
69
Recomenda-se sempre usar padrões de projeto adequados para manter
a coesão e baixo acoplamento para esse tipo de código específico.
Além disso, existe uma estrutura do Java 7, conhecida como bloco “try-with-resources”,
que permite declarar e inicializar objetos que implementam o método “AutoCloseable”.
Dessa forma, ao término do bloco “try”, o próprio compilador insere instruções para cha-
mar o fechamento (“close”) desses recursos, além de se precaver em relação a exceções
que possam surgir. Assim, o código fica mais organizado e o escopo da variável “cone-
xao” fica limitado ao bloco “try”:
try(Connection conexao = ConnectionFactory.criaconexao()) {
// operações que podem lançar exceptions runtime e SQLException
} catch(SQLException e) {
System.out.println(e);
}
Para ampliar o seu conhecimento veja o material complementar da Unidade 2,
disponível na midiateca.
MIDIATECA
70
Neste item vamos apresentar a criação de uma aplicação Java com persistên-
cia de dados e uso da biblioteca JDBC. Serão utilizados exemplos de acesso a
banco de dados MySQL, utilizando os modelos de projeto DAO e “Factory” para
implementar operações CRUD e mapeamento objeto-relacional não automático.
Criação do projeto Java para acesso ao banco de dados MySQL
Em seguida, apresentaremos a criação de um novo projeto no ambiente de de-
senvolvimento NetBeans, como já mostrado nas disciplinas anteriores POO1 e
POO2. O projeto NetBeans será usado para integração da linguagem Java com
o banco de dados MySQL criado (“dbpoo3”) para realização de consultas, inclu-
são e exclusão de registros, criação de tabelas, campos, entre outros.
Inicialmente será criado um projeto Java chamado “JDBC Application”.
NA PRÁTICA
71
Em seguida, será configurada a conexão com o banco de dados na janela “ser-
viços” no menu principal Windows > Services.
Inicialmente é necessário verificar se o driver JDBC está instalado de forma cor-
reta e confirmar o caminho onde o arquivo da biblioteca JAR foi instalado:
Em seguida, é necessário confirmar se a string de conexão ao MySQL Server
está adequada e testar se a conexão será realizada com sucesso:
72
Importante
Neste exemplo, foi necessária a modificação da string de conexão “default” su-
gerida após a instalação do MySQL devido a problemas de inconsistência no
“timezone” do servidor.
Após o teste de conexão ao MySQL Server, é possível verificar as informações
do schema “dbpoo3” e sua estrutura de dados, tabelas e registros:
73
Em seguida, vamos configurar o diretório “Library” do projeto JDBC_Aplication
para podermos utilizar o driver de conexão ao MySQL Server. Usando a opção
do menu principal Tools > Libraries, pode ser configurada a biblioteca “MySQL
JDBC Driver” usando a ferramenta “Library Manager”:
74
Após a criação da biblioteca MySQL JDBC Driver, basta adicioná-la no diretório
“Library” do projeto JDBC_Application.
A próxima etapa é criar no projeto JDBC_Application as classes mostradas no
Tópico 1: classe “Connection_Factory”, para a criação das conexões ao banco
de dados; classe “JavaBeans”, para o controle dos registros da tabela “Alunos”;
e classe “AlunoDAO”, para gerenciar os métodos CRUD para acesso, consulta e
alterações no banco de dados “dbpoo3”. Ao final, criaremos uma classe de teste
Java JDBC_Applicationpara realizar a conexão ao banco de dados “dbpoo3“ e
uma consulta à tabela “Alunos”:
Iniciamente, criar a classe “Connection_Factory” no projeto JDBC_Application.
75
Em seguida, digitar o código da classe “Connection_Factory”.
Atenção
É importante notar que no código para conexão com o MySQL Server estamos
utilizando a string de conexão testada anteriormente e a senha da conta “root”
cadastrada na instalação do sistema.
A próxima etapa é criar uma classe de teste “JDBC_Application” (1), digitar
o código no processo “main” para testar a abertura da conexão da classe
“Connection_Factory” (2), executar o código com a opção “run” (3) e verificar o
resultado na janela “output” (4).
76
Em seguida, criar a classe “Aluno” usando o conceito JavaBeans para represen-
tar o modelo de entidade da tabela “Alunos” do banco de dados “dbpoo3” visto
anteriormente. Os campos da tabela “Alunos” serão os atributos da classe “Alu-
no” com seus métodos “getters” e “setters”:
A próxima etapa é criar a classe “AlunoDAO” com seus métodos:
• Método construtor “AlunoDAO”, que chama método de “ConnectionFactory”
para criar uma conexão.
• Método “adicionaAluno” para adicionar registros na tabela “Alunos” do banco
de dados.
77
Método “getListaAlunos()” para listagem dos registros da tabela “Alunos”:
Métodos de modificação e exclusão de registros de alunos.
78
Por fim, podemos implementar classes de teste para os métodos da classe
“AlunoDAO”. Segue exemplo de teste de listagem dos alunos usando o método
“getListaAlunos( )”.
79
Resumo da Unidade 2
Nesta unidade abordamos as técnicas para desenvolver aplicações Java com persistên-
cia de dados com uso do JDBC. Também apresentamos exemplos de acesso a banco de
dados, modelo DAO para implementar operações CRUD e mapeamento objeto-relacional
não automático.
Mostramos técnicas de controle de concorrência em transações de bancos de dados
multiusuários a fim de manter a consistência e a integridade das informações.
Por fim, foram abordadas recomendações para o tratamento adequado de erros e exce-
ções em sistemas Java com acesso a banco de dados via JDBC.
80
Referências
ALVES, W. P. Banco de Dados. São Paulo: Érica-Saraiva, 2014. ISBN: 978-85-365-1896-1.
DEITEL, P. Java: como programar. 10. ed. São Paulo: Pearson Education do Brasil, 2017.
ISBN: 978-85-430-0479-2. Biblioteca Virtual.
MANZANO, J. A. N. G.; COSTA JUNIOR, R. A. Java 7 - Programação de computadores:
guia prático de introdução, orientação e desenvolvimento. São Paulo: Érica, 2011. ISBN:
978-85-365-1933-3. Minha Biblioteca.
ORACLE JAVA DOCUMENTATION. Lesson: JDBC Basics. Disponível em: https://docs.
oracle.com/javase/tutorial/jdbc/basics/. Acesso em: 5 set. 2020.
https://docs.oracle.com/javase/tutorial/jdbc/basics/
https://docs.oracle.com/javase/tutorial/jdbc/basics/
Persistência de dados via JPA
e Hibernate (MOR automático)
UNIDADE 3
82
Nesta unidade apresentaremos as ferramentas de mapeamento objeto-relacional –MOR,
ou, em inglês, object relational mapping – ORM, cujo objetivo é simplificar a integração
entre as aplicações e os sistemas de banco de dados.
Enquanto os sistemas de banco de dados utilizam modelos relacionais baseados em
linguagem SQL, as aplicações normalmente utilizam modelagem orientada a objetos e
linguagens de programação distintas (Java, C++ etc.), tornando complexa a integração
entre estas duas abordagens de sistemas.
As ferramentas MOR funcionam como uma interface entre as aplicações e os bancos
de dados, possibilitando a implementação dos processos de persistência em bancos de
dados de forma mais simples, padronizada e automatizada.
INTRODUÇÃO
Nesta unidade você será capaz de:
• Utilizar persistência de dados por meio de mapeamento objeto-relacional –
MOR automático.
OBJETIVO
83
Ferramentas de mapeamento objeto-
relacional - MOR (JPA e Hibernate)
A técnica de mapeamento objeto-relacional – MOR foi desenvolvida para facilitar a in-
tegração entre a modelagem orientada a objetos, normalmente usada nas aplicações,
e a modelagem relacional usada dos gerenciadores de bancos de dados – SGBD.
Como esses modelos possuem formas diferentes de estruturação dos dados, torna-se
necessário realizar transformações nas informações a serem utilizadas em cada mo-
delagem.
Dessa forma, as ferramentas MOR, como Hibernate, trabalham como uma camada en-
tre a lógica da aplicação e os sistemas de bancos de dados, de forma a conseguir auto-
matizar várias atividades de persistência dos dados.
As interfaces MOR são padronizadas pela biblioteca Java Persistence API – JPA para
gerar compatibilização com outras funcionalidades da plataforma Java e facilitar a utili-
zação dos recursos de persistência de dados.
A aplicação passa, então, a relacionar-se com a interface MOR, e não mais diretamente
com a base de dados, tornando a lógica da aplicação independente de SGDBs especí-
ficos. Como não é mais necessário escrever diretamente em linguagem SQL, as opera-
ções CRUD em banco de dados tornam-se mais simples, aumentando a produtividade
da equipe de programação. Além disso, as transformações de tipos de dados entre o
SGDB e a aplicação podem ser tratadas pela própria interface MOR.
Nos exemplos mencionados nesta unidade vamos trabalhar com a biblioteca
Java Persistence API –JPA, a ferramenta MOR Hibernate e o sistema ge-
renciador de banco de dados MySQL.
84
Figura: Mapeamento Objeto-Relacional.
Fonte: Notas de aula do professor (2020).
Inicialmente, para configurar o Hibernate é preciso criar o arquivo “persistence.xml” na
pasta META-INF no diretório “classpath” da aplicação Java. O arquivo “persistence.xml”
possui informações sobre endereço URL, login e senha de conexão com o banco de dados,
juntamente com outras informações da biblioteca JPA, como mostra o exemplo a seguir.
<?xml version=”1.0” encoding=”UTF-8”?>
<persistence
version =”2.1”
xmlns =” http://xmlns.jcp.org/xml/ns/persistence”
xmlns:xsi = “http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation = “http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd”>
<persistence-unit name = “BD_teste-pu” transaction-type = “RESOURCE_LOCAL”>
<provider> org.hibernate.ejb.HibernatePersistence
<properties>
<property name = “hibernate.dialect” value =
“org.hibernate.dialect.MySQL5InnoDBDialect” />
SGBD = Modelo relacional
Base de Dados
Objetivo 1 Objetivo 2
Objetivo 3
Aplicação - modelo OO
Camada de
mapeamento MOR
85
<property name = “hibernate.hbm2ddl.auto” value = “create” />
<property name = “ javax.persistence.jdbc.driver” value =
“com.mysql.jdbc.Driver” />
<property name =” javax.persistence.jdbc.user” value= “root” />
<property name =” javax.persistence.jdbc.password” value = “root” />
<property name =” javax.persistence.jdbc.url” value =
“ jdbc:mysql://localhost:3306/BD_teste” />
</properties>
</persistence-unit>
</persistence>
Mapeamento objeto-relacional
As ferramentas MOR possuem como uma das metas mais importantes a implementa-
ção de um mapeamento adequado entre o modelo relacional do banco de dados e os
conceitos de orientação a objetos.
O mapeamento objeto-relacional define as transformações que devem ser realizadas nos
dados para permitir o fluxo bidirecional entre a aplicação e o banco de dados. Esse ma-
peamento também:
• Define como a ferramenta MOR pode implementar consultas complexas no ban-
co de dados, envolvendo várias tabelas e registros.
• Pode ser implementado a partir de arquivos XML ou usando anotações Java.
A utilização de anotações específicas nos próprios arquivos Java é mais prática, pois
evita-se o emprego de extensos arquivos em outra linguagem e sintaxe XML. As ano-
tações Java de mapeamento objeto-relacional estão no pacote “ javax.persistence” da
bibliotecaJPA.
86
Figura: Modelo de Mapeamento Objeto-Relacional – MOR.
Fonte: Notas de aula do professor (2020).
Entidades
A anotação @Entity deve estar sempre antes do nome de uma classe que terá objetos
persistidos no banco de dados. Qualquer classe com a anotação @Entity será mapeada
para uma tabela do banco de dados. O nome da tabela será o mesmo nome da classe
por padrão, mas poderá ser alterado pela anotação @Table.
Além disso, cada instância de uma classe com a anotação @Entity deve ter um identi-
ficador único com anotação @Id, que é atributo numérico geralmente com tipo “Long”.
@Entity
class Aluno {
@Id
private Long id ;
}
Considere:
• Uma classe com a anotação @Entity terá seus atributos mapeados para colunas
na tabela que corresponde à classe mapeada.
• As colunas também possuem por padrão os mesmos nomes dos atributos, mas
é possível modificar usando a anotação @Column.
SGBD
Modelo
Orientação a
Objeto
Modelo
Relacional Aplicação
Java
87
Devido à anotação @Id, a coluna que corresponde ao atributo id será definida como cha-
ve primária da tabela.
@Entity
@Table(name = “tbl_alunos”)
class Aluno{
@Id
@Column (name=”aluno_id”)
private Long id ;
}
Restrições para atributos das entidades
Seguem as principais propriedades da anotação @Column para definição de restrições e
regras para os atributos das entidades:
length: define a quantidade máxima de caracteres de uma strings.
nullable: define se o campo pode possuir valores nulos (“null”).
unique: define se uma coluna pode ter valores repetidos ou não.
precision: define a quantidade de dígitos de um número decimal para armazenamento.
scale: define a quantidade de casas decimais de um número decimal.
Geração automática de chaves primárias
Em geral, a anotação @GeneratedValue é utilizada logo após a anotação @Id para indicar
que o banco deve gerar automaticamente os valores de uma chave primária simples
e numérica.
@Entity
class Aluno {
@Id
@GeneratedValue
private Long id ;
}
88
Considere:
• A anotação @GeneratedValue sem parâmetros adicionais significa que será usa-
da a estratégia GenerationType.AUTO.
• Nesse caso, a implementação do JPA é que escolherá a estratégia mais adequa-
da para gerar os valores da chave primária conforme o banco de dados em uso.
No exemplo a seguir, a estratégia automática é explicitamente escolhida. Observe:
@Entity
class Aluno {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id ;
}
O parâmetro “strategy” da anotação @GeneratedValue aceita os seguintes valores:
GenerationType.AUTO
De acordo com o banco de dados em uso, a biblio-
teca JPA decide a estratégia mais adequada para a
geração da chave primária.
GenerationType.IDENTITY
De acordo com o banco de dados em uso, a biblio-
teca JPA mapeia o atributo para uma coluna identi-
dade para gerar valores distintos.
GenerationType.SEQUENCE
De acordo com o banco de dados em uso, a biblio-
teca JPA utiliza o recurso de sequências para gerar
valores distintos.
GenerationType.TABLE
Para gerar valores distintos, essa estratégia usa
uma tabela de dados auxiliar.
89
Mapeamento automático
É necessário que as ferramentas MOR implementem mapeamentos adequados entre os
tipos de dados da aplicação Java e os diferentes conjuntos de tipos de dados usados em
cada SGBDs.
Temos uma lista dos tipos básicos do Java que são mapeados automaticamente para
tipos correspondentes do banco de dados. São eles:
1. Tipos primitivos (byte, short, char, int, long, float, double e boolean).
2. Classes Wrappers (Byte, Short, Character, Integer, Long, Float, Double e Boolean).
3. String.
4. BigInteger e BigDecimal.
5. java.util.Date e java.util.Calendar.
6. java.sql.Date, java.sql.Time e java.sql.Timestamp.
7. Array de byte ou char.
8. Enums.
9. Serializables.
Objetos grandes (large objects – LOB)
Para a utilização de dados com maiores volume de informação e complexidade, como
livros-texto ou arquivos de vídeo multimídia, deve-se aplicar a anotação @LOB em atri-
butos dos tipos String, byte[ ], Byte[ ], char[ ] ou Character[] para que a biblioteca JPA im-
plemente os procedimentos de manipulação específicos e a escolha dos tipos de dados
mais adequados para cada banco de dados.
A anotação @GeneratedValue, juntamente com a anotação @Id, deve ser usa-
da apenas em um único atributo ou propriedade para permitir que a aplicação
tenha portabilidade para outros bancos de dados.
Como existem funcionalidades que não estão implementadas em to-
dos os bancos de dados, as estratégias GenerationType.IDENTITY e
GenerationType.SEQUENCE não são consideradas portáveis.
Importante
90
Dados transientes
Quando existem atributos de um determinado conjunto de objetos que não devem ser ar-
mazenados no banco de dados de forma persistente, deve-se usar a anotação @Transient.
Modos de acesso ao estado das entidades - Field Access e
Property Access
Para permitir o gerenciamento adequado das entidades entre o modelo relacional dos
bancos de dados e o modelo de orientação a objetos das aplicações, é necessário que
os provedores de JPA tenham acesso ao estado de cada entidade.
No JPA são especificados dois modos de acesso ao estado das instâncias das entidades
pelo JPA:
1. Field Access: neste modo de acesso, as anotações de mapeamento são incluídas
e não é necessário implementar métodos “getters” e “setters”, pois o acesso aos atri-
butos dos objetos é feito diretamente a partir da técnica de “reflection”. O provedor
JPA não usará os métodos “getters” e “setters”, mesmo se estiverem implementados.
2. Property Access: neste modo de acesso, é necessário implementar os métodos
“getters” e “setters”, além de incluir as anotações Java nos respectivos atributos.
O provedor JPA usará os métodos “getters” e “setters” para acessar e modificar o
estado dos objetos.
Se a informação de determinado atributo pode ser calculada a partir de outros
dados armazenados no banco de dados, esse atributo pode ser marcado com
a anotação @Transient para que não seja armazenado de forma desnecessária.
Exemplo
91
Gerando as tabelas do banco de dados
É possível gerar as tabelas do banco de dados usando a biblioteca JPA com as informa-
ções descritas no arquivo “persistence.xml” e as anotações colocadas nas classes “Java”.
As tabelas são geradas por meio do seguinte método estático da classe “Persistence”:
createEntityManagerFactory(string persistenceUnit)
em que o parâmetro “persistenceUnit” permite definir a unidade de persistência descrita
no arquivo “persistence.xml”.
Gerenciador de entidades (EntityManager)
Para gerenciar as entidades de uma aplicação é necessário usar um objeto gerenciador
“EntityManager” obtido a partir de uma classe “EntityManagerFactory”.
Aqui, vamos descrever alguns métodos do EntityManager para manipulação de dados
em uma aplicação Java.
Armazenamento
de informações no banco
de dados
É necessário usar o método “persist()” do gerenciador
EntityManager para persistir as informações de um objeto
no banco de dados.
Busca de informações do
banco de dados
Para obter um objeto que contenha informações do banco
de dados, é possível utilizar os métodos “find()” ou “getRe-
ference()” do EntityManager. Enquanto o método “find()”
busca imediatamente os dados, o método “getReference()”
adia a consulta até que um método “get” seja executado no
objeto desejado.
Remoção
de registros
É possível usar o método “remove()” do gerenciador de enti-
dade EntityManager para remover um registro de um objeto.
Atualização
de objetos
Podem-se usar os métodos “setters” do próprio objeto para
atualizar seus registros correspondentes.
92
Listagem
de objetos
É possível utilizar a linguagem de consulta JPQL da JPA
para listar os objetos de uma tabela. Além de similar ao
SQL, a sintaxe da linguagem não se altera, mesmo para di-
ferentes bancos de dados.
Abertura de
transações
Para sincronizar o banco de dados com as informações que
forammodificadas em memória, é necessário criar uma
transação JPA pelo gerenciador de entidade que controla
os objetos a serem atualizados. Para abrir uma transação
JPA, deve-se usar o método “begin()”. Para sincronizar as
informações com o banco de dados de forma apenas par-
cial, pode-se usar o método “flush()”. Para a sincronização
de dados definitiva, deve-se usar o método “commit()”.
93
Pool de conexões, lazy load e relacionamentos
Pool de conexões
Em um sistema com acesso a banco de dados, quando a quantidade de usuários se
torna muito grande, é necessário implementar um controle de gerenciamento eficiente
das conexões com o banco de dados. O sistema pode ficar muito lento se houver muitas
requisições de acesso simultâneo ao banco de dados, além do problema de controlar a
abertura e o fechamento das conexões. Para evitar esse tipo de problema podemos utili-
zar o conceito de “pool de conexões”.
O conceito consiste em uma camada entre banco de dados e o cliente que solicita
as conexões.
Em uma aplicação Java, o banco de dados pode ser representado por uma conexão
JDBC para algum SGBD ou servidor de banco de dados. Por outro lado, o cliente pode
ser representado por uma classe “Java” ou um componente específico Web (Enterprise
JavaBeans – EJB, Servlet ou Action Struts).
O conceito do “pool de conexões” é similar ao da “fábrica de conexões”, porém com muito
mais recursos. Nesse caso, o cliente utiliza o “pool” para gerar as conexões necessárias
com o banco em vez de criá-las diretamente utilizando o driver JDBC. Assim, é possível:
• Diminuir o acoplamento.
• Aumentar a coesão do código da aplicação Java deixando a responsabilidade de
gerenciamento das conexões com o banco de dados para essa camada intermediária.
Como a responsabilidade de abertura e fechamento das conexões com o banco
de dados é do “pool de conexões”, este pode manter uma quantidade adequada
de conexões abertas com o banco de dados. A partir desse gerenciamento
independente de conexões, é possível otimizar o custo computacional de solici-
tação de aberturas e fechamentos sucessivos ao banco de dados.
Exemplo
94
Quando o cliente Java solicita um novo acesso ao banco de dados, o “pool” consegue
usar uma das conexões “disponíveis” já abertas com o banco de dados. Em seguida,
a conexão fica com o estado “alocada” para aquele determinado cliente Java. Ao final,
quando o cliente terminar a transação com o banco de dados, o “pool” conseguirá man-
ter a conexão aberta, marcando seu estado como “disponível” para um novo acesso ao
banco de dados, como visto na figura a seguir.
Figura: Funcionamento do “Pool” de Conexões.
Fonte: Notas de aula do professor (2020).
Considere:
a) Pool de conexões gerenciando três conexões físicas: Cliente 1 e Cliente 2 alocados e
uma conexão livre disponível.
b) Cliente 3 solicita acesso e recebe a conexão disponível do pool.
c) Cliente 1 finaliza transação, e o pool desconecta o Cliente 1, mas mantém a conexão
aberta disponível para outros clientes.
Algumas das vantagens da utilização do “pool de conexões”:
• Evita o custo computacional de abrir e fechar as conexões reais com o banco de
dados sucessivamente, tornando o aplicativo mais rápido e eficiente.
Connection Pool
n=3
Cliente 1
Cliente 1
Cliente 2
Cliente 2
Cliente 2
Cliente 3
Cliente 3
a)
b)
c)
Base de
dados
Base de
dados
Base de
dados
95
• Permite atender a uma grande quantidade de requisições ao banco de dados
mantendo uma quantidade menor de conexões reais com o banco. Entretanto, a
quantidade de conexões simultâneas ao banco de dados precisa ser tão grande
quanto a quantidade de conexões do pool.
Pool de conexões com o Hibernate
Para executar operações CRUD no banco de dados com o Hibernate é necessário usar
o objeto “Session” a partir de uma instância da classe “SessionFactory”. Esta instância é
usada para construir um objeto “Session”, que é usado para implementar as operações
CRUD no banco de dados.
SessionFactory sessions = …; // Obtém uma “session factory” a partir da configuração
do Hibernate.
Session session = sessions.openSession(); // Abre uma “session” a partir da classe
“SessionFactory”.
Para realizar as operações CRUD será necessário criar uma conexão com o banco de
dados, preferencialmente por meio de um pool de conexões. Para utilizar o pool de co-
nexões interno do Hibernate, é preciso configurar os parâmetros da string de conexão
com o banco de dados. Além disso, é necessário informar a quantidade máxima de co-
nexões permitidas ao banco de dados, conforme as propriedades descritas a seguir:
• hibernate.connection.driver_class – Classe do driver JDBC instanciada.
• hibernate.connection.url – Endereço URL de conexão JDBC.
• hibernate.connection.username – Login do usuário do banco de dados.
• hibernate.connection.password – Senha do usuário no banco de dados.
• hibernate.connection.pool_size – Número máximo de conexões abertas pelo pool.
LAZY LOAD e EAGER LOAD
Modos de carregamento “Lazy” e “Eager”
Os gerenciadores de entidades controlam o carregamento do estado dos objetos e ad-
ministram cada instância de uma entidade. Existem duas formas de fazer a carga de um
objeto com as informações de um banco de dados:
• Modo “Lazy” (ou modo “preguiçoso”): o provedor retarda o máximo possível a
consulta das informações no banco de dados.
96
• Modo “Eager” (ou modo “ansioso”): as informações são prontamente carregadas
do banco de dados.
Métodos de carregamento
Os métodos “find()” e “getReference()” permitem que uma aplicação, a partir das identi-
dades dos objetos, consiga carregar as instâncias das respectivas entidades. A diferença
entre esses métodos é:
• “find()” tem características do modo “Eager”, e os dados do objeto são carregados
imediatamente.
• “getReference()” possui características do modo “Lazy”, e o carregamento dos da-
dos do objeto é adiado o máximo possível.
Fetch type – tipos básicos
Em certas ocasiões, se um objeto possui atributos grandes demais e seu carregamento é
desnecessário e custoso, deve ser possível indicar ao provedor que esse comportamento
é desejável.
Quando é usado o método “getReference()” para buscar as informações de um
objeto no banco, os dados são carregados no modo “Lazy”, ou seja, apenas
quando o estado desse objeto é acessado pela primeira vez.
Exemplo
Ao acessar um objeto do tipo “Disciplina”, pode ser que não seja necessário
carregar todo o conteúdo da disciplina, mas apenas alguns atributos principais.
Neste caso, é possível informar ao provedor JPA a partir do atributo “fetch” da
anotação “@Basic” que não é preciso carregar todos os atributos.
@Basic ( fetch = FetchType.LAZY )
protected String getNome () {
return nome ;
}
Exemplo
97
Relacionamentos
No paradigma da orientação a objetos, quando uma instância de um objeto precisa
utilizar recursos disponíveis por outro objeto, utilizamos a técnica de relacionamentos.
Os relacionamentos entre as instâncias das entidades são implementados por meio de
vínculos entre classes. Além da identificação de um relacionamento, é necessário tam-
bém definir a cardinalidade desse relacionamento, ou seja, a multiplicidade dos relacio-
namentos. Conforme descrito na biblioteca JPA, existem algumas formas de relaciona-
mento. São elas:
• One to one (um para um): uma instância de uma classe está relacionada a apenas
uma instância de outra classe. Por exemplo, uma cidade possui apenas um prefeito
e um prefeito governa apenas uma cidade.
• One to many (um para muitos): neste relacionamento, uma instância de uma classe
está relacionada a várias instâncias de outra classe. Por exemplo, uma empresa pos-
sui muitos departamentos e cada departamento pertence a apenas uma empresa.
O modo de carregamento preguiçoso, ou “Lazy”, somente será permitido pelos
gerenciadores de entidades para a carga de atributos básicos caso o modo de
acesso seja Property Access.
Importante
Cidade A
Cidade B
Prefeito 1
Prefeito 2
EmpresaA
Departamento 1
Departamento 2
Departamento 3
98
• Many to one (muitos para um): para este relacionamento, várias instâncias de
uma classe estão relacionadas a somente uma instância de outra. Por exemplo, um
depósito bancário pertence a apenas um cliente e um cliente faz muitos depósitos.
• Many to many (muitos para muitos): para este relacionamento, várias instâncias
de uma classe estão relacionadas a várias instâncias de outra. Por exemplo, um livro
possui muitos autores e um autor escreve muitos livros.
Agora, apresentaremos com mais detalhes cada uma delas.
One to one (um para um)
Considere que neste domínio existam duas entidades: Cidade e Prefeito. É preciso criar
uma classe para cada entidade e colocar em cada uma as anotações de mapeamento.
@Entity
class Cidade {
@Id
@GeneratedValue
private Long id ;
}
Cliente A
Depósito 1
Depósito 2
Depósito 3
Autor A
Livro 1
Autor B
Livro 2
Livro 3
99
@Entity
class Prefeito {
@Id
@GeneratedValue
private Long id ;
}
Como existe um relacionamento entre “Cidade” e “Prefeito”, devemos expressar esse víncu-
lo a partir de um atributo que pode ser inserido na classe “Cidade”. Também é necessário
informar ao provedor JPA o relacionamento do tipo one to one entre uma “Cidade” e um
“Prefeito”, colocando a anotação @OneToOne no atributo que expressa o relacionamento:
@Entity
class Cidade {
@Id
@GeneratedValue
private Long id ;
@OneToOne
private Prefeito prefeito;
}
Neste caso, a tabela da classe “Cidade” possui uma coluna de relacionamento chamada
“ join column”. Essa coluna de relacionamento é definida como uma chave estrangeira
relacionada à tabela da classe “Prefeito”. A “ join column” tem seu nome formado pelo
atributo que estabelece o relacionamento, seguido pelo caractere “_” e pelo atributo da
chave primária da entidade-alvo. No exemplo de “Cidade” e “Prefeito”, o nome da coluna
de relacionamento seria “prefeito_id”.
É possível alterar o nome padronizado das “ join columns” usando a anotação
“@JoinColumn”, de acordo com o exemplo a seguir:
@Entity
class Cidade {
@Id
@GeneratedValue
private Long id ;
@OneToOne
@JoinColumn (name = “prefeito _ id”)
private Prefeito prefeito ;
}
100
One to many (um para muitos)
Considere que no domínio existam as entidades “Empresa” e “Departamento”. Devem
ser criadas duas classes com as anotações básicas de mapeamento. Devido ao vín-
culo entre “Empresa”e “Departamento”, é necessário informar esse relacionamento por
meio de um atributo incluído na classe “Departamento”. Considerando que uma “Em-
presa” possa ter muitos “Departamentos”, devemos usar uma coleção para representar
esse relacionamento. Para expressar a cardinalidade do relacionamento entre “Empresa”
e “Departamentos”, devemos usar a anotação “@OneToMany” na coleção.
Além das tabelas das classes “Empresa” e “Departamento”, seria preciso uma terceira ta-
bela para relacionar os registros das “Empresas” com os registros dos “Departamentos”.
Como no caso anterior, essa nova tabela é denominada tabela de relacionamento ou
“ join table”. Nesse tipo de relacionamento, a “ join table’ é nomeada com a junção do
caracter “_” com os nomes das duas entidades relacionadas. No exemplo de “Empresa”
e “Departamento”, o nome da tabela de relacionamento seria “Empresa_Departamento”
e possuirá duas colunas vinculadas às entidades que formam o relacionamento. No
exemplo, a “ join table” Curso_Aluno possuirá uma coluna chamada “curso_id” e outra
chamada “alunos_id”.
Nesse caso também é possível usar a anotação “@JoinTable” no atributo que define o
relacionamento para personalizar os nomes das colunas e da própria “ join table”.
Many to one (muitos para um)
Neste tipo de relacionamento, as entidades “Depósito” e “Cliente” seriam modeladas por
duas classes com as anotações principais de mapeamento para estas entidades.
É necessário mostrar a relação entre depósitos e clientes a partir de um atributo que pode
ser inserido na classe “Deposito”. Por isso, devemos utilizar um atributo simples para ex-
pressar o relacionamento de que um “Deposito” pertence a apenas um único cliente.
É necessário usar a anotação “@ManyToOne” no atributo a fim de mostrar a cardinalida-
de entre depósitos e clientes.
A tabela da classe Depósito contém uma coluna de relacionamento associada à tabela
da classe Cliente. O nome da “ join column” é padronizado por meio da entidade destino
do relacionamento, em seguida usando o caractere “_” e por fim o atributo da chave pri-
mária da entidade destino. No caso acima de depósitos e clientes, o nome de coluna de
relacionamento seria padronizado por “cliente_id” . Entretanto, esse nome padrão poderia
ser alterado pela anotação “@JoinColumn”.
101
Many to many (muitos para muitos)
Considere o domínio com entidades “Livro” e “Autor”, com suas respectivas classes con-
tendo as anotações básicas de mapeamento. Como existe um relacionamento entre li-
vros e autores, é necessário definir esse vínculo a partir de um atributo que pode ser in-
serido na classe “Autor”. Como um autor pode escrever vários livros, pode ser necessário
usar uma coleção para informar esse tipo de relacionamento.
É necessário usar a anotação “@ManyToMany” na coleção para informar a cardinalidade
desse relacionamento entre livros e autores.
Além das tabelas correspondentes às classes “Livro” e “Autor” no banco de dados, é
necessária a criação de uma “ join table” para relacionar os registros dos autores com
os dos livros. O nome dessa tabela é formado pela concatenação do caracatere “_” e os
nomes das duas entidades. No exemplo anterior, o nome dessa nova “ join table” seria
“Livro_Autor”, contendo duas colunas denominadas “livro_id” e “autor_id” vinculadas às
entidades que formam o relacionamento.
Para personalizar os nomes da “ join table” e de suas respectivas colunas, é possível usar
a anotação “@JoinTable” no atributo que define o relacionamento.
Relacionamentos bidirecionais
Se um relacionamento é definido por um atributo em uma das entidades, é possível
acessar a outra entidade a partir da primeira.
Uma instância da primeira entidade não precisa de relacionamento a uma ins-
tância da segunda entidade em relações do tipo “many to one”. É preciso incluir
o atributo “optional” da anotação “ManyToOne” para garantir que cada instância
da primeira entidade esteja relacionada a uma instância da segunda entidade.
Importante
102
Objetos embutidos
Considere que no domínio haja uma entidade chamada “Aluno” e que todo aluno possua
um endereço composto por vários atributos como logradouro, número, complemento,
município, CEP etc.
Pode ser interessante criar uma classe “Endereço”, separada da classe “Aluno”, para orga-
nizar a estrutura de dados da aplicação.
Nesse caso, duas tabelas serão criadas, “Endereços” e “Alunos”, uma para cada classe.
Na tabela “Alunos” é necessária uma coluna de relacionamento com a tabela “Endere-
ços”. Para acessar os dados do endereço de um aluno, as duas tabelas precisariam ser
consultadas por meio de uma operação “ join”.
Como esse tipo de operação no banco de dados consome muitos recursos, poderia
ser mais eficiente se os endereços dos alunos fossem armazenados na própria tabela
“Alunos”, tornando a tabela “Endereços” desnecessária. Entretanto, para manter o modelo
de objetos com as classes “Aluno” e “Endereço” separadas, pode ser feito da seguinte
forma: na classe “Aluno”, deve ser removida a anotação de cardinalidade “@OneToOne”,
e, na classe “Endereço”, a anotação “@Entity” deve ser trocada por “@Embeddable”. Além
disso, não será definida uma chave primária para a classe “Endereço”, pois ela não define
uma entidade.
Herança
Herança é um dos conceitos de orientação a objetos mais difíceis de serem mapeados
no modelo relacional de banco de dados. A representação da hierarquiaentre superclas-
ses e subclasses no modelo de banco de dados pode ser bastante complexa.
Considere o relacionamento entre carro e motor. Como o relacionamento está
definido na classe “Carro”, podemos acessar o objeto motor a partir de um
objeto carro.
Exemplo
103
Existem três formas de mapeamento de herança definidos na biblioteca JPA. São elas:
1. Single Table (Tabela Única).
2. Joined (Tabelas “Join”).
3. Table Per Class (Tabela por Classe).
A seguir, vamos detalhar cada forma de mapeamento de herança. Preste atenção!
1. Single Table
Essa forma de mapeamento é bem simples e permite melhor eficiência nas consultas.
No mapeamento “Single Table”, deve ser colocada a seguinte anotação na superclasse:
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
Nesse mapeamento, o armazenamento dos dados dos objetos criados a partir da su-
perclasse ou das subclasses deverá ser feito por apenas uma tabela a ser criada pelo
provedor JPA. Essa tabela terá o nome da superclasse por padrão, e todos os atributos
da hierarquia de classes serão mapeados para colunas dessa tabela única. Também será
criada uma coluna específica denominada “DTYPE” para identificar qual classe do objeto
correspondente ao registro.
Um dos problemas do mapeamento “Single Table” é a utilização exagerada
de espaço no banco de dados, pois na tabela criada nem todos os campos
(colunas) são usados para todos os registros.
2. Joined
No mapeamento “Joined”, para cada classe da hierarquia é criada uma tabela, em que
são adicionados apenas os campos relativos aos atributos da classe correspondente.
Uma subclasse pode ter atributos diferentes de outra subclasse, mas a tabela
a ser criada no mapeamento final possuirá colunas representando todos os
atributos das subclasses.
Exemplo
104
Para refazer os objetos quando uma consulta for executada e implementar a relação
entre os registros das diversas tabelas, os campos das tabelas das subclasses possuem
chaves estrangeiras relacionadas à tabela da superclasse.
Uma vantagem da estratégia “Joined” é a menor utilização de espaço do
que a estratégia “Single Table”. Entretanto, uma desvantagem é que as con-
sultas são mais lentas devido ao fato de que é preciso executar operações
“join” com alto custo de processamento nas tabelas para recuperar os da-
dos dos objetos.
3. Table Per Class
Nessa forma de mapeamento é criada uma tabela para cada classe concreta da hierar-
quia de classes. Entretanto, como os atributos de um objeto não ficam em tabelas dife-
rentes, não é preciso fazer operações lentas de “ join” para remontar um objeto.
Uma desvantagem desse mapeamento é não existir no banco de dados
uma relação explícita entre as tabelas correspondentes às classes da hie-
rarquia. Além disso, no mapeamento “Table Per Class” não se pode usar a
geração de chaves primárias simples e numéricas de forma automática.
105
Gerenciador de estados, caching e con-
sultas JPQL
Conforme definido na ferramenta JPA, as instâncias das entidades são administradas pe-
los Gerenciadores de Entidade (Entity Managers), cujas responsabilidades principais são:
Gerenciar o estado
dos objetos.
Sincronizar os dados da
aplicação e do banco de dados.
Gerenciamento do estado dos objetos
Com o objetivo de conhecer como os objetos são administrados pelos Gerenciadores
de Entidade, precisamos conhecer o ciclo de vida das entidades. Os seguintes estados
podem ser ocupados por uma instância de uma entidade:
• Novo (New): o objeto não está associado a um Gerenciador de Entidade. No es-
tado “Novo”, o objeto não possui uma chave de identidade, e seu conteúdo não é
transmitido para o banco de dados. Se uma instância de uma entidade tiver sido re-
cém-criada com um comando “New”, ela se encontrará no estado “Novo”, conforme
a especificação JPA.
• Administrado (Managed): nesse estado o objeto está associado a um Gerencia-
dor de Entidade e possui uma chave de identidade. Os dados de um objeto no esta-
do “Administrado” são atualizados no banco de dados a cada sincronização.
• Desvinculado (Detached): no estado “Desvinculado” o objeto não está associado
a um Gerenciador de Entidade, mas possui uma chave de identidade. O conteúdo de
um objeto nesse estado não é sincronizado com o banco de dados.
• Removido (Removed): nesse estado o objeto possui uma chave de identidade e
está associado a um Gerenciador de Entidade. Quando houver uma sincronização,
o conteúdo desse objeto será removido do banco de dados.
106
Sincronização com o banco de dados
Todas as operações de modificação, inclusão e retirada de informações em entidades
são controladas por um Gerenciador de Entidades (“Entity Manager”).
Temos que considerar algumas ações:
1. Inicialmente, essas operações nas entidades são realizadas em memória.
2. Em seguida, precisam ser propagadas para persistência no banco de dados a
partir de uma atividade de sincronização.
Quando ocorre uma atividade de sincronização, as alterações dos objetos em estado
“Administrado” são enviadas para o banco de dados e os registros dos objetos em estado
“Removido” são eliminados do banco. Além disso, conforme a especificação JPA, uma
sincronização apenas pode ser realizada se houver uma transação ativa.
Cada Gerenciador de Entidades possui apenas uma transação associada e, para recupe-
rá-la, é necessário usar o método “getTransaction()”.
Agora:
3. Em seguida, é possível ativar a transação a partir do método “begin()”.
4. Por fim, deve-se usar o método “commit()” para confirmar a transação e realizar
uma sincronização com o banco de dados.
5. A transação é finalizada.
Outra forma de realizar uma sincronização com uma transação ativa é a partir do mé-
todo “flush()”. Nessa forma de sincronização, os dados são propagados para o banco
de dados, mas não ficarão visíveis para outras transações. Somente em consultas efe-
tuadas dentro da própria transação é que esses dados serão considerados. Dentro de
uma mesma transação podem ocorrer várias chamadas ao método “flush()”, podendo
ser efetuadas.
Qualquer alteração efetuada pelo método “flush()” pode ser desfeita no banco de dados
utilizando o método “rollback()”, o qual também finaliza a transação.
107
Figura: Ciclo de vida das entidades - Estados de acesso.
Fonte: Notas de aula do professor (2020).
Flush Mode
Existem duas formas de sincronismo usadas pelos provedores JPA:
FlushModeType.AUTO (padrão) FlushModeType.COMMIT
Antes de uma operação de consulta,
a JPA executa uma sincronização
automática no banco de dados para
que as alterações realizadas, mas
que ainda estiverem em memória, se-
jam usadas na consulta.
Nesse modo, o funcionamento da sincro-
nização não está definido e cada prove-
dor pode implementar o método de sin-
cronização mais adequado.
É possível configurar o FlushModeType para apenas uma consulta ou todas as consultas
realizadas por meio de um Entity Manager.
Transições
Apresentaremos as principais transições que ocorrem em cada instância de uma entidade.
Novo → Gerenciado (New → Managed)
Um objeto passa do estado “Novo” (“New”) para o estado “Gerenciado” (“Managed”)
quando é usado o método “persist()” dos Gerenciadores de Entidade (“Entity Managers”).
Gerenciador de Entidades
Administrado (Managed)Novo
(New)
Removido (Removed)Desvinculado (Detached)
Base de
dados
108
BD → Gerenciado (BD → Managed)
O provedor JPA cria objetos no estado “Gerenciado” (“Managed”) para armazenar as in-
formações quando são recuperadas do banco de dados – BD.
Gerenciado → Desvinculado (Managed → Detached)
Se um objeto no estado “Gerenciado” (“Managed”) não necessita ser controlado, basta
executar o método “detach()” para que seu Gerenciador de Entidade seja desvinculado.
Dessa forma, se o objeto estiver no estado desvinculado o sincronismo não será mais
realizado com o banco de dados.
Se for utilizado o método “clear()”, todos os objetos administrados por um Gerenciador de
Entidade serão colocados em estado “Desvinculado” (“Detached”) —é preciso usar o
método “clear()”. Todos os objetos controlados por um Gerenciador de Entidade também
passarão para o estado desvinculado se for utilizado o método “close()”.
Desvinculado → Gerenciado (Detached → Managed)
O estado de um objeto desvinculado pode ser transmitido para um objeto gerenciado
com a mesma identidade usando o método “merge()”. Nesse caso, os dados serão nova-
mente sincronizados com o banco de dados.
Gerenciado → Removido (Managed → Removed)
As informações de um objeto não são apagadas do banco de dados que passa do estado
“Gerenciado” para o estado “Desvinculado”. Quando o método “remove()” é executado,
o objeto é registrado para ser removido do banco de dados. O conteúdo desse objeto
somente será removido do banco de dados quando o provedor executar uma operação
de sincronismo.
Gerenciado → Gerenciado (Managed → Managed)
Caso ocorra alguma alteração dos dados no banco de dados, o conteúdo de uma ins-
tância no estado “Gerenciado” poderá ficar desatualizado. Dessa forma, para a atuali-
zação de uma instância gerenciada com as informações do banco de dados, pode-se
utilizar o método “refresh()”.
109
Figura: Transições entre os estados das entidades.
Fonte: Entity Object Life Cycle (2020).
Caching
Com o objetivo de possibilitar acesso mais rápido às informações que são mais utiliza-
das e que por sua vez não são alteradas com muita frequência, é mais eficiente manter
uma cópia dessas informações em memória de acesso rápido (“cache”), evitando aces-
so constante e mais lento aos dispositivos de armazenamento do banco de dados.
Existem algumas formas de implementação de cache em provedores JPA. Vamos co-
nhecê-las a seguir:
• Cache de primeiro nível (“Persistence Context”): uma instância será armaze-
nada no cache de primeiro nível sempre que for carregada por um Gerenciador de
Entidade. Se uma aplicação procurar uma instância que já estiver disponível no
“Persistence Context” correspondente, a busca será feita na memória cache rápida,
e não utilizando o processo mais lento do banco de dados. Também é importante
notar que cada gerenciador possui um cache de primeiro nível.
• Cache de segundo nível (“Shared Cache”): este tipo de cache não é exclusi-
vo para apenas um Gerenciador de Entidade, sendo compartilhado pelos demais
gerenciadores. Para habilitar o “Shared Cache” em uma aplicação JPA, é preci-
so acrescentar um elemento para configuração da “persistent unit” no arquivo
“persistence.xml”.
O elemento pode habilitar diferentes configurações para o cache de segundo nível, como
mostrado seguir:
• ALL: o “Shared Cache” é ativado para todas as entidades.
new
persist
persistremove
clear/
close
flush/
commit
retrieve
find, query,...
New Managed
Detached Removed
Database
https://www.objectdb.com/java/jpa/persistence/managed
110
• ENABLE_SELECTIVE: somente para as entidades com a anotação
@Cacheable(true) é ativado o “Shared Cache”.
• DISABLE_SELECTIVE: o “Shared Cache” é ativado para todas as entidades, exce-
to para as entidades com a anotação @Cacheable(false).
• NONE: o “Shared Cache” é desativado para todas as entidades.
• UNSPECIFIED: a implementação do “Shared Cache” depende do provedor JPA.
Consultas JPQL
Os bancos de dados podem executar consultas de maneira eficiente baseadas no mo-
delo relacional. Entretanto, como as aplicações são baseadas em modelagem orientada
a objetos, os resultados das consultas devem ser baseados nesse paradigma, e não no
modelo relacional tradicional.
Dessa forma, a biblioteca JPA oferece ferramentas para realizar consultas de dados no
paradigma da orientação a objetos. Em especial, a biblioteca JPA 2 especifica duas for-
mas para implementar consultas nesse paradigma:
• 1ª forma: usa a linguagem JPQL (Java Persistence Query Language) específica
para consultas.
• 2ª forma: implementa as consultas usando uma biblioteca Java específica.
Outra justificativa para usar as ferramentas de consulta da biblioteca JPA 2 é a indepen-
dência das formas de consulta ao banco de dados. É possível definir uma consulta em
JPQL e executá-la em um banco de dados qualquer instalado no provedor JPA.
Consultas dinâmicas
Em qualquer método de uma classe Java pode ser definida uma consulta do tipo dinâmi-
ca em linguagem JPQL. Nesse caso, pode-se executar o método “createQuery()” e utilizar
como parâmetro uma string JPQL para implementar uma consulta dinâmica.
public void MetodoConsulta() {
String str_jpql= “SELECT x FROM Aluno x”;
Query consulta = manager.createQuery(str_jpql) ;
}
É importante notar que toda vez que um método que cria uma consulta dinâmica for
executado, o provedor processará o código JPQL dessa consulta. Como essa caracterís-
111
tica das consultas dinâmicas pode diminuir a eficiência da aplicação, é possível utilizar
um tipo alternativo de consultas, denominado “Named Query”, que, apesar de ter menor
flexibilidade, não prejudica a performance da aplicação.
E como realizar a consulta por Named Queries?
Um consulta “Named Query” somente é processada na inicialização da “Persistent Unit”,
unidade de persistência, ao contrário de uma consulta dinâmica. Para melhorar a efi-
ciência das consultas “Named Query”, os provedores JPA podem mapeá-las em “Stored
Procedures” já compiladas no banco de dados .
A definição desse novo tipo de consulta pode ser feita com a anotação “@NamedQuery”
nas classes que implementam essas entidades. Quando é necessário definir várias con-
sultas de uma vez pode-se usar a anotação “@NamedQueries”.
Observe o código a seguir, que define uma consulta “Named Query”:
@NamedQuery (name =”Aluno.findAll”, consulta = “SELECT x FROM Aluno x”)
class Aluno {
. . .
}
Na sequência, é mostrado o código de consultas com a anotação “@NamedQueries”:
@NamedQueries({
@NamedQuery (name =”Aluno.findAll”, consulta = “SELECT x FROM Aluno x”),
@NamedQuery (name =”Aluno.count”, consulta = “SELECT COUNT(x) FROM Aluno x”)
})
class Aluno {
// código da classe Aluno . . .
}
É preciso utilizar o método “createNamedQuery()” para que seja executada uma
“Named Query” a fim de retornar uma consulta existente para ser utilizada. Por outro
lado, as consultas Named Queries são criadas na inicialização da “Persistence Unit”.
public void listaAlunos() {
Query consulta = manager.createNamedQuery(“Aluno.findAll”) ;
List <Aluno> alunos = consulta.getResultList();
}
112
Parâmetros de consultas JPQL
É recomendável parametrizar as consultas em JPQL para que se tornem mais genéricas
e também para impedir problemas de segurança, como o “SQL Injection”.
Para implementar essa parametrização das consultas JPQL, inicialmente é necessário
acrescentar um caractere “:” seguido ao nome do argumento desejado. Depois, antes de
implementar uma consulta parametrizada, é necessário definir os valores a serem usa-
dos nos argumentos.
@NamedQuery ( name =”Aluno.findByMatricula”,
query =”SELECT x FROM Aluno x WHERE x.matricula> : matricula “)
public void listaAlunos() {
Query consulta = manager.createNamedQuery (“Aluno.findByMatricula”);
consulta.setParameter (“Matricula”, 2020);
List <Aluno> alunosComMatriculasDe2020 = consulta.getResultList();
}
Caso seja necessário, novos parâmetros podem ser acrescentados de forma ordinal em
uma consulta por meio da utilização do caractere “?” e, em seguida, um número.
@NamedQuery (name =”Aluno.findByMatricula”,
query =”SELECT x FROM Pessoa x WHERE x.matricula> ?1”)
public void listaAlunos() {
Query consulta = manager.createNamedQuery (“Aluno.findByMatricula”);
consulta.setParameter (1, 2020);
List <Aluno> alunosComMatriculasDe2020 = consulta.getResultList();
}
Tipos de resultado
Lista de objetos de uma entidade
O resultado de uma consulta JPQL com filtros de pesquisa usando o método
“getResultList()” retorna uma lista com as instâncias persistidas de uma entidade. Essa
consulta retorna uma lista das instâncias dosobjetos no estado “Gerenciado” (“Managed”).
113
Isto é, as mudanças implementadas nesses objetos estão atualizadas no banco de da-
dos conforme as regras de sincronização.
String consulta = “SELECT x FROM Aluno x”;
Query consulta = manager.createQuery(consulta);
List <Aluno> alunos = consulta.getResultList();
Lista de objetos comuns
Muitas vezes não é necessário retornar todas as informações de um objeto em uma
consulta em uma listagem de objetos. Pode ser que, em certos casos, seja necessário o
retorno de apenas alguns atributos. Assim, é possível definir qual atributo que cada con-
sulta deve retornar do banco de dados.
Exemplo: a consulta a seguir gera uma lista completa de objetos “aluno”.
String consulta = “SELECT x FROM Aluno x”;
TypedQuery <Aluno> consulta = manager.createQuery(consulta, Aluno.class);
List <Aluno> alunos = consulta.getResultList();
Em seguida, uma nova consulta recupera apenas os nomes dos objetos “aluno”.
String consulta = “ SELECT x.nome FROM Aluno x”;
TypedQuery <String> consulta = manager.createQuery (consulta, String.class);
List <String> nomes = consulta.getResultList();
Funções de agregação em consultas
Em certas ocasiões, o resultado de uma consulta não deve ser uma lista de objetos, mas
sim um valor numérico referente a alguma operação matemática de soma, média ou
contagem de objetos. Seguem alguns exemplos de funções de agregação para imple-
mentar essas operações:
• AVG: retorna a média aritmética de um conjunto de dados numéricos.
• COUNT: retorna a contagem de registros de um conjunto de dados.
• MAX: retorna o maior valor de um grupo de dados numéricos.
• MIN: retorna o menor valor de um grupo de dados numéricos.
• SUM: retorna a soma aritmética de um grupo de dados numéricos.
114
Resultados especiais em consultas
Em certos casos, as consultas aos bancos de dados podem retornar resultados com-
plexos, geralmente em forma de uma lista de “array” de objetos. Muitas vezes, é preci-
so controlar essa lista de “array” para alterar a posição e a visualização dos dados em
uma consulta.
• Criação de classes com o operador “New”: é possível usar o operador “New”
no código JPQL para criar uma classe com o objetivo de modelar o resultado de
uma consulta e reconfigurar a posição dos dados na lista de “array”. Nesse caso, a
aplicação não precisa mais usar um “array” de objetos para controlar a posição dos
itens de uma consulta mais complexa, pois será usada uma classe específica para
essa finalidade.
• Paginação de consultas: o conceito de paginação consiste em dividir uma úni-
ca consulta que retornaria uma grande quantidade de dados em algumas consul-
tas menores. Dessa forma, ao receber os dados, aos poucos se evitará usar uma
grande quantidade de memória, diminuindo-se o tráfego na rede. Os métodos
setFirstResult() e setMaxResults() podem ser usados para configuração dos parâ-
metros de paginação de uma consulta ao banco de dados.
Para ampliar o seu conhecimento veja o material complementar da Unidade 3,
disponível na midiateca.
MIDIATECA
115
No desenvolvimento de um aplicativo de banco de dados com multiusuários
pode ocorrer problema de concorrência. Quando dois Gerenciadores de Entida-
de trabalham com objetos da mesma entidade e com o mesmo identificador,
pode ser gerado um resultado com erro.
Por exemplo, suponha que os seguintes trechos de código sejam executados
em paralelo.
Trecho 1
// Depósito na conta
manager1.getTransaction().begin() ;
Conta x = manager1.find(Conta.class, 1L) ;
x.setSaldo ( x.getSaldo() + 200 ) ;
manager1.getTransaction().commit() ;
Trecho 2
//Saque na conta
manager2.getTransaction().begin() ;
Conta y = manager2.find(Conta.class, 1L) ;
y.setSaldo ( y.getSaldo() - 200 ) ;
manager2.getTransaction().commit() ;
Considere que:
• O primeiro trecho acrescenta 200 reais ao saldo da conta com identificador 1.
• O segundo trecho retira 200 reais da mesma conta.
Dessa forma, o saldo dessa conta deve possuir o mesmo valor antes e depois des-
ses dois trechos de código serem executados. Contudo, dependendo da ordem na
qual as linhas dos dois trechos são executadas, o resultado pode ser outro.
NA PRÁTICA
116
Agora, suponha que o valor inicial do saldo da conta com identificador 1 seja
1.000 reais e as linhas dos dois trechos sejam executadas na seguinte ordem:
manager1.getTransaction().begin();
manager2.getTransaction().begin();
Conta x = manager1.find(Conta.class,1L); // x: saldo = 1000
x.setSaldo(x.getSaldo() + 200); // x: saldo = 1200
Conta y = manager2.find(Conta.class,1L); // y: saldo = 1000
y.setSaldo(y.getSaldo() - 200) ; // y: saldo = 800
manager1.getTransaction().commit(); // Conta 1: saldo = 1200
manager2.getTransaction().commit(); // Conta 1: saldo = 800
Nesse caso, o saldo final seria 800 reais em vez do valor desejado de 1.000
reais. Contudo, temos algumas soluções para enfrentar o problema: Locking
Otimista e Locking Pessimista.
1. Locking Otimista
Para solucionar o problema da concorrência entre Gerenciadores de Entidade,
pode ser aplicado o conceito de “Locking Otimista”. Nesse caso, deve ser incluí-
do na entidade um atributo adicional para determinar a versão dos registros.
Esse atributo deve ser anotado com “@Version” e seu tipo deve ser “short”, “int”,
“long”, “Short”, “Integer”, “Long” ou “ java.sql.Timestamp”.
@Entity // Conta.java
public class Conta {
@Id
@GeneratedValue
private Long id;
private double saldo;
@Version
private Long versao;
// Métodos GETTERS AND SETTERS
}
Sempre que um Gerenciador de Entidade tentar refazer a operação que altera
um registro da tabela correspondente à classe “Conta”, o campo referente ao
atributo anotado com “@Version” será atualizado. Antes de modificar um regis-
tro da tabela referente à classe “Conta”, os Gerenciadores de Entidade compa-
ram a versão do registro no banco de dados com a do objeto que eles possuem.
117
Se as versões forem iguais, nenhum outro Gerenciador de Entidade terá alte-
rado o registro, e, então, as mudanças poderão ser implementadas sem pro-
blemas. Por outro lado, se as versões forem diferentes, outro Gerenciador de
Entidade alterou o registro, e assim as alterações serão abortadas com o lança-
mento de uma exceção. Por fim, as aplicações devem tentar refazer a operação
capturando essa exceção.
2. Locking Pessimista
Outra forma de lidar com o problema da concorrência entre Gerenciadores de
Entidade é o “Locking Pessimista”. Nessa abordagem, um Entity Manager pode
“travar” os registros, fazendo com que os outros Gerenciadores de Entidade que
precisem alterar os mesmos registros tenham que esperar. Há outras formas
de usar o “Locking Pessimista”, por exemplo: é possível passar um parâmetro
adicional quando um objeto é buscado pelo método “find()”.
Conta x = manager.find(Conta.class, 1L , LockModeType.PESSIMISTIC_WRITE) ;
Uma grande dificuldade em usar “Locking Pessimista” é a possibilidade de ge-
rar um “deadlock”. Por exemplo, dois Gerenciadores de Entidade podem procu-
rar o mesmo objeto na mesma “thread” usando o “Locking Pessimista”, como
mostrado a seguir.
(Linha 1) Conta x = manager1.find(Conta.class,1L,LockModeType.PESSIMISTIC_
WRITE);
(Linha 2) Conta y = manager2.find(Conta.class,1L,LockModeType.PESSIMISTIC_
WRITE);
(Linha 3) manager1.commit(); // NUNCA VAI EXECUTAR ESSA LINHA
Considere que:
• Na linha 1, o primeiro Gerenciador de Entidade “trava” a conta com iden-
tificador 1 e esse objeto só será liberado na linha 3.
• Na linha 2, o segundo Gerenciador de Entidade vai esperar o primeiro
liberar o objeto, impedindo que a linha 3 seja executada.
• A linha 3 nunca será executada. Depois de certo tempo esperando na
linha 2, o segundo Gerenciador de Entidade lança uma exceção.
118
Resumo da Unidade 3
Nesta unidade vimos como utilizar ferramentas de mapeamento objeto-relacional –
MOR automático para implementarsistemas de informações com persistência de dados.
Também foram abordadas técnicas para a utilização de “pool de conexões” e os tipos de
carregamento de dados, além da compreensão dos diferentes tipos de relacionamentos
entre entidades de dados.
Além disso, foram apreendidas as funcionalidades dos “Entity Managers” para o geren-
ciamento dos estados de entidades e suas transições, e foram apresentados os diferen-
tes tipos de caching para melhorar a eficiência dos aplicativos. Por fim, falamos sobre a
linguagem JPQL para implementar consultas aos bancos de dados.
119
Referências
ADVANCED JPA tutorial with Hibernate. Jstobigdata. 27 ago. 2019. Disponível em:
https://jstobigdata.com/jpa/advanced-jpa-tutorial-with-hibernate/. Acesso em: 10 set. 2020.
DEITEL, P. Java: como programar. 10. ed. São Paulo: Pearson Education do Brasil, 2017.
ISBN: 978-85-430-0479-2. Biblioteca Virtual.
HIGOR. Introdução à JPA – Java Persistence API. Plataforma Devmedia. 2013. Dispo-
nível em: https://www.devmedia.com.br/introducao-a-jpa-java-persistence-api/28173.
Acesso em: 10 set. 2020.
MANZANO, J. A. N. G.; COSTA JÚNIOR, R. A. Java 7 – Programação de computadores:
guia prático de introdução, orientação e desenvolvimento. São Paulo: Érica, 2011.
ISBN: 978-85-365-1933-3. Minha Biblioteca.
ADVANCED JPA tutorial with Hibernate. Jstobigdata. 27 ago. 2019. Disponível em: https://jstobigdata.com/jpa/advanced-jpa-tutorial-with-hibernate/. Acesso em: 10 set. 2020.
DEITEL, P. Java: como programar. 10. ed. São Paulo: Pearson Education do Brasil, 2017. ISBN: 978-85-430-0479-2. Biblioteca Virtual.
HIGOR. Introdução à JPA – Java Persistence API. Plataforma Devmedia. 2013. Disponível em: https://www.devmedia.com.br/introducao-a-jpa-java-persistence-api/28173. Acesso em: 10 set. 2020.
MANZANO, J. A. N. G.; COSTA JÚNIOR, R. A. Java 7 – Programação de computadores: guia prático de introdução, orientação e desenvolvimento. São Paulo: Érica, 2011. ISBN: 978-85-365-1933-3. Minha Biblioteca.
https://www.devmedia.com.br/introducao-a-jpa-java-persistence-api/28173
JavaServer Faces
UNIDADE 4
121
As aplicações Web implementam a interação com os usuários por meio de navegado-
res de rede (browsers), muitas vezes utilizando o protocolo de comunicação HTTP para
acessar as páginas da aplicação escritas em linguagem HTML.
Como geralmente as aplicações Web são executadas em ambiente multiusuário, as pá-
ginas HTML também precisam ser geradas de forma dinâmica, pois cada usuário pode
estar requisitando uma informação diferente do sistema. Dessa forma, o conteúdo de
cada página precisa ser gerado dinamicamente e de forma eficiente pela aplicação Web.
Assim, a equipe de programação Java precisará aprender a trabalhar também com o
protocolo HTML de forma eficiente para o desenvolvimento das aplicações Web. Com
o objetivo de facilitar o processo de manutenção das páginas HTML e melhorar a le-
gibilidade do código Java da aplicação Web, foi desenvolvida a tecnologia JavaServer
Faces – JSF. Com o JSF, não será necessário colocar o conteúdo dinâmico HTML junta-
mente com as classes Java. Com essa tecnologia, será possível utilizar o código HTML
de forma mais direta e eficiente.
INTRODUÇÃO
Nesta unidade você será capaz de:
• Compreender o JavaServer Faces – JSF para o desenvolvimento da camada de
visão para aplicações Web em linguagem Java.
OBJETIVO
122
Visão geral do JavaServer Faces – JSF
O framework JavaServer Faces – JSF é uma das principais ferramentas para auxiliar o
desenvolvimento de aplicativos Java na Web. Como a estrutura do JSF é baseada nos
padrões de projeto MVC e Front Controller, é importante realizarmos uma revisão nesses
conceitos, que foram vistos anteriormente.
As páginas HTML de uma aplicação Web precisam ser geradas de forma dinâmica, prin-
cipalmente em um ambiente multiusuário. Para o desenvolvimento de sistemas com-
putacionais com essas características, são necessários conhecimentos específicos em
diversas áreas, tais como: programação em linguagens Java, HTML e SQL, protocolos de
rede HTTP, bancos de dados relacionais, entre outros.
Com o objetivo de auxiliar o desenvolvimento de aplicativos Web, a plataforma Java dis-
põe de uma solução genérica chamada Web Container, a qual é responsável por oferecer
os recursos para a aplicação, que são:
• O controle de mensagens de requisição e resposta no protocolo HTTP.
• A permissão de acesso multiusuário de forma eficiente.
• A geração de páginas de forma dinâmica na aplicação Web.
Figura: Diagrama de um Web Container.
Fonte: Elaborada pelo autor (2020).
Página 1
Página 2
Resposta HTTP
Requisição HTTP
Resposta HTTP
Requisição HTTP
WEB Container
Aplicação Web
123
Especificações Java EE, Servlets e JavaServer Pages
Java Enterprise Edition (Java EE) é uma plataforma para desenvolvimento de servidores
Web usando a linguagem de programação Java. Ela fornece uma interface de programa-
ção para a execução de aplicações Web multicamadas, com escalabilidade, segurança
e confiabilidade.
A interface Servlet (“pequeno servidor”) é uma classe Java que acrescenta novas fun-
cionalidades de um servidor Web. A API Java Servlet presente no pacote “ javax.servlet”
possibilita implementar conteúdo dinâmico nas páginas de uma aplicação Web utilizan-
do a plataforma Java.
É possível usar um servidor de aplicação Java EE com diversos Web Containers disponí-
veis no mercado, como Tomcat, Jetty, WildFly, JBoss, GlassFish ou WebSphere. Apesar
das especificações-padrão, cada Web Container possui diferenças em cada uma de suas
configurações, as quais devem ser ajustadas pelos desenvolvedores.
JavaServer Pages – JSP
JSP é uma tecnologia de visualização de alto nível de Servlets Java que é executada
na máquina do servidor. JavaServer Pages são traduzidas em tempo de execução em
Servlets Java, sendo armazenadas em memória cache para reúso até o momento em
que a página original for alterada.
Para obter a versão mais atual do Java EE (versão 8), consulte a página Java
Community Process por meio do endereço eletrônico: https://jcp.org/en/jsr/de-
tail?id=366.
Ampliando o foco
A especificação Servlet faz parte do Java EE e atualmente está na versão
3.1. Para obtê-la, consulte a página Java Community Process, disponível no
endereço: https://jcp.org/en/jsr/detail?id=340.
Ampliando o foco
https://jcp.org/en/jsr/detail?id=366
https://jcp.org/en/jsr/detail?id=366
https://jcp.org/en/jsr/detail?id=340
124
A tecnologia JSP permite escrever um modelo de página em linguagens do lado do clien-
te (como HTML, CSS, JavaScript, entre outras coisas) e suporta “taglibs”, que são apoia-
dos por partes do código Java que permitem controlar dinamicamente o fluxo da página.
A tecnologia JSP também suporta Expression Language, que pode ser usada para aces-
sar dados por meio de atributos disponíveis nos escopos de página, solicitação, sessão
e aplicativo, principalmente em combinação com as “taglibs”.
Os arquivos JSP podem conter apenas código HTML ou acrescentar código Java para
que as páginas tenham comportamento dinâmico. Dessa forma, podem-se declarar va-
riáveis e testes condicionais, e implementar laços (“loops”) e outras estruturas usando a
linguagem Java.
Basicamente, uma página JSP é um arquivo em linguagem HTML com a
extensão “*.jsp”.
No exemplo a seguir, será criada uma página JSP usando HTML simples e um pouco
de código Java para exibir a mensagem “Alô, mundo!”. Inicialmente, será declarada uma
variável do tipo “String” inicializada com a mensagem desejada, colocando o código Java
entre as tags <% e %>.
O código Java escrito entre as tags <% e %> em uma página JSP é chamado de Scriptlet.
É possível utilizar algumas variáveis implícitas disponíveis no framework JSP. Por exem-
plo, para imprimir um resultado usando o método “println”, é possível usar uma variável
“out” do tipo JspWriter, como mostrado a seguir:
<% out.println(nome); %>
Outro exemplo é: para incluir comentários em uma página JSP, que devemser implemen-
tados entre as tags <%-- e --%>:
<%-- incluir comentário na JSP --%>
<html>
<body>
<% String mensagem = “Olá, mundo!”; %>
</body>
Exemplo
125
Muitas vezes, é necessário declarar que uma página JSP precisa acessar classes Java
que estão disponíveis em pacotes que precisam ser importados. Nesse caso, são utiliza-
das diretivas para a configuração específica de uma página com a sintaxe a seguir:
<%@ page import=“endereço do pacote” %>
Com esta tag, informamos qual configuração é necessária nessa página, e o atributo
“import” informa a definição do pacote que deve ser importado.
Padrão de arquitetura MVC
O padrão de arquitetura MVC, ou Modelo-Visão-Controlador (Model-View-Controller), é
muito usado em Java e em outras plataformas de desenvolvimento para separar a lógica
de apresentação do modelo de negócio de uma aplicação.
Com a utilização desse padrão de arquitetura, uma aplicação é dividida por tipos de com-
ponentes, a saber:
• Modelo (Model): componente responsável pelo encapsulamento dos dados e
das funcionalidades da aplicação.
• Visão (View): componente responsável por exibir as informações gráficas, cujos
dados são obtidos do componente “Modelo”.
• Controlador (Controller): componente responsável por receber as requisições do
usuário e, em seguida, acionar os demais componentes “Modelo” e “Visão”.
Figura: Exemplo do modelo MVC para desenvolvimento de aplicações Web
com JSP/Servlet/EJB/JPA.
Fonte: Elaborada pelo autor (2020).
Model
Sessions Beans
(EJB)
Entity Classes
(JPA)
Request
Response
Database
Controller
(servlet)
Client
(browser)
View
(JSP pages)
126
Padrão Front Controller
No padrão de arquitetura Front Controller, as requisições do usuário são recebidas pelo
mesmo componente, que pode implementar todas as tarefas necessárias, evitando que
o código tenha muita repetição e facilitando a manutenibilidade do sistema.
Aplicação Web Java
Vejamos um exemplo da estrutura de pastas padrão que colocamos em uma aplicação
Java em um Web Container.
Figura: Estrutura de pastas de uma aplicação Web Java.
Fonte: Elaborada pelo autor (2020).
A estrutura de pastas necessárias para implantação da aplicação Web em um Web
Container é criada automaticamente pelas ferramentas de desenvolvimento – IDEs.
Considere:
• Abaixo da pasta raiz da aplicação Web, existe uma subpasta chamada WEB-INF,
cujo conteúdo os usuários da aplicação não conseguem acessar.
• A subpasta WEB-INF contém o arquivo “web.xml”, que possui as configurações do
Web Container.
• Abaixo da pasta WEB-INF existe a subpasta “classes”, que contém o código com-
pilado da aplicação, e a subpasta “lib”, que contém os arquivos das bibliotecas extras
(arquivos com extensão “*.jar”) que serão usadas pela aplicação.
App-Web
classes
WEB-INF
lib
web.xml
127
Configuração de uma aplicação Web JSF
Cada aplicação Web JSF deve manter a estrutura de pastas da aplicação Web, como
vimos anteriormente.
Agora, apresentaremos alguns arquivos de configuração da aplicação Web JSF. Observe.
Arquivo “web.xml”
No arquivo “web.xml”, a configuração do atributo “Faces Servlet” pode indicar a classe do
JSF que será implementada e o padrão de endereço de documento “URL”, o qual estará
relacionado a essa “Servlet”.
No exemplo a seguir, cada requisição com padrão de endereço “URL” com o sufixo “.xhtml”
será processada pelas “Faces Servlet”.
Nesse código, é informado ao JSF que deve ler todas as páginas com formato XHTML e
transformar JSF em HTML caso seja necessário.
Para conhecer a descrição detalhada da estrutura de pastas e uma aplicação
Web Java, visite a página Java Community Process por meio do endereço
eletrônico: https: //jcp.org/en/jsr/detail?id=340.
Ampliando o foco
<servlet>
<servlet-name>Faces Servlet </servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet </servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml </url-pattern>
</servlet-mapping>
Exemplo
https: //jcp.org/en/jsr/detail?id=340
128
Arquivo “faces-config.xml”
Devemos adicionar um arquivo chamado “faces-config.xml” no diretório WEB-INF. Nes-
se arquivo, podemos alterar diversas configurações do JSF, mas, a princípio, podemos
deixá-lo apenas com a tag “faces-config”.
Bibliotecas
Muitas vezes é preciso que a aplicação tenha uma implementação da biblioteca
JavaServer Faces para poder usar as funcionalidades JSF. Geralmente, quando é usado
um servidor de aplicação Java EE, a inclusão de uma implementação JSF é feita automa-
ticamente. Caso contrário, uma implementação desta biblioteca pode ser acrescentada
de forma manual na subpasta “lib” da aplicação Web.
<?xml version =“1.0” encoding =“UTF-8”?>
<faces-config
xmlns = “http://xmlns.jcp.org/xml/ns/javaee”
xmlns:xsi = “http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation = “http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd”
version = “2.2”>
</faces-config >
Exemplo
129
Managed Beans, requisições e navegação
Managed Beans
“Managed Bean” é uma classe regular JavaBean gerenciada pela estrutura JSF. Essa
classe contém os métodos-padrão “getter” e “setter”, lógica de negócios ou, até mesmo,
objeto de apoio que contém todo o valor do formulário HTML.
Os “Managed Beans” funcionam como modelo para o componente da interface do usuá-
rio e podem ser acessados na página JSF. Além disso, eles podem ser registrados por
arquivo de configuração JSF ou usando-se anotações, tornando mais fáceis o gerencia-
mento de requisições e a navegação na aplicação Web.
Os objetos “Managed Beans” são importantes em uma aplicação JSF e possuem as se-
guintes funcionalidades:
• Executar as tarefas necessárias à aplicação conforme as ações dos usuários.
• Fornecer as informações que serão exibidas nas telas da aplicação JSF.
• Receber as informações que serão enviadas nas requisições.
Processo de criação de um Managed Bean
Um objeto “Managed Bean” pode ser criado inicialmente a partir de uma classe Java.
Em seguida, é preciso fazer o registro do “Managed Bean” arquivo de configuração
“faces-config.xml”, definindo:
• O nome.
• A classe.
• O escopo do objeto.
O nome do “Managed Bean” é usado para acessá-lo nas páginas Web da aplicação.
A seguir, veja um exemplo de registro de um “Managed Bean”:
130
A partir da versão JSF 2, também é possível criar o objeto “Managed Bean” a começar de
uma classe Java com a anotação “@ManagedBean” usando o pacote “ javax.faces.bean”.
Utilizando esta anotação, o nome do “Managed Bean” será definido por padrão como o
nome da classe com a primeira letra minúscula e será selecionado o escopo “request”.
Propriedades do Managed Bean
É necessário que seja definido um método “get” de leitura para acessar um atributo em
uma tela JSF, conforme o padrão de nomenclatura da linguagem Java. Por outro lado,
para modificar um atributo com os dados recebidos de uma página Web, é necessário
que seja definido um método “set” de escrita.
Vejamos um exemplo de um “Managed Bean” com métodos de leitura e escrita de um
atributo numérico.
<managed-bean>
<managed-bean-name>testeBean </managed-bean-name>
<managed-bean-class>TesteBean </managed-bean-class>
<managed-bean-scope>request </managed-bean-scope>
</managed-bean>
Exemplo
@ManagedBean
public class TesteManagedBean {
private int valor;
public int setNumero(int valor) {
this.valor = valor;
}
public int getNumero() {
return valor;
}
}
Exemplo
131
Usando o padrão Expression Language (#{}) e os métodos de leitura e escrita já im-
plementados, é possível mostrar o valor do atributo numérico do exemplo “Managed
Bean” anterior. Além disso, caso seja necessário modificar o valor do atributo numérico
do exemplo anterior, será possível associar na página HTML um formulário (“form”) comuma caixa de entrada de texto (“inputText”) e um botão de comando (“commandButton”)
para executar a modificação, conforme mostrado a seguir.
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns = “http://www.w3.org/1999/xhtml”
xmlns:h= “http://java.sun.com/jsf/html”>
<h:head>
<title> Teste UVA </title>
</h:head>
<h:body>
Valor: #{testeManagedBean.numero}
<h:form >
<h:inputText value =“#{testeManagedBean.numero”/>
<h:commandButton value = “Modifica”/ >
</h:form>
</h:body>
</html>
É necessário reforçar que a vinculação com uma propriedade de um
“Managed Bean” não ocorre pelo nome do atributo, e sim usando-se os nomes
dos métodos-padrão “getters” e “setters”. No exemplo anterior, o nome do atri-
buto numérico “valor” não foi usado para gerar o vínculo.
Utilizou-se o nome dos métodos-padrão “getNumero” e “setNumero” para gerar
o vínculo #{testeManagedBean.numero}.
Importante
132
Implementação de ações
Quando um usuário executar alguma ação em qualquer elemento da página, como clicar
em botão, caixa de seleção ou link, será necessário implementar os métodos nas clas-
ses correspondentes dos “Managed Beans”. Esses métodos deverão realizar as lógicas
a serem executadas a cada ação do usuário. Eles poderão ter retorno nulo (“void”) se
for necessário manter os usuários em uma mesma tela do aplicativo. Caso seja preciso
implementar alguma navegação entre diferentes telas, o método poderá retornar algum
valor para permitir essa funcionalidade.
Vejamos um exemplo de código com método para aumentar um atributo numérico.
Será necessário criar uma página com um botão de comando para executar o incre-
mento do atributo do exemplo anterior do TesteManagedBean. Em seguida, será preciso
associar ao método “aumentaNumero()”, conforme o código mostrado a seguir.
<h:form>
<h:commandButton value=“Aumenta” action=“#{testeManagedBean.aumentaNumero}” />
</h:form>
Por padrão, o JSF considera que o nome da classe seja o mesmo do objeto “Managed
Bean”, porém com a primeira letra em caixa-baixa quando é usada a anotação “@Mana-
gedBean”. Caso seja necessário modificar o nome de classe, poderá ser adicionado o
seguinte argumento na anotação “@ManagedBean”:
@ManagedBean
public class TesteManagedBean {
private int valor;
public void aumentaNumero(){
this.valor = valor + 1;
}
// Métodos GETTERS e SETTERS
}
Exemplo
133
@ManagedBean (name = “classe_teste”)
class TesteManagedBean {
// código
}
Processamento de requisições
Depois da implementação de uma aplicação Web em um Web Container, o processa-
mento de requisições inicializa o processamento de todas as requisições e respostas em
cada página, além de permitir que a aplicação seja acessada por multiusuários.
Em seguida, o conteúdo das páginas da aplicação Web pode ser gerado automaticamen-
te usando a interface “Servlet” por meio das seguintes etapas:
Sempre que uma requisição HTTP é feita para o endereço URL definido pela anotação
@WebServlet, a execução do método “service()” é realizada.
Devemos considerar:
• O primeiro parâmetro desse método é a referência à instância da classe
“HttpServletRequest”, a qual guarda todas as informações da requisição.
• O parâmetro seguinte do método “service()” é a referência da instância da classe
“HttpServletResponse”, na qual o conteúdo gerado pela Servlet será salvo.
• Em seguida, será necessário utilizar os métodos “getWriter()” e “println()” para
acrescentar alguma mensagem na resposta HTTP a ser transmitida ao navegador
do usuário, conforme o exemplo a seguir.
1
Criar uma classe Java. 1ª etapa
2
Utilizar a herança da superclasse “javax.servlet.http.HttpServlet”.2ª etapa
3
Reescrever o método “service()”.3ª etapa
4
Definir o endereço URL a ser usado no acesso para a “Servlet”
pela anotação @WebServlet.
4ª etapa
134
Processamento de uma requisição JSF
Logo após receber uma requisição feita por um navegador em uma aplicação JSF, a
classe Faces Servlet deve controlar a execução de algumas etapas. Vejamos quais são
elas logo a seguir.
Restore View
Nesta etapa, uma árvore de componentes (“component tree”) inicial representando a
tela da primeira requisição feita por um usuário é gerada. A árvore de componentes da
tela será reconstruída nas requisições seguintes desse mesmo usuário (“postback”).
Apply-Request Values
Nesta etapa, a árvore de componentes construída na etapa anterior, “Restore View”,
é verificada e decodificada, quando cada componente retira as informações asso-
ciadas da requisição atual para atualização. Além disso, os eventos de usuário são
identificados nessa etapa e são armazenados para tratamento futuro na etapa
“Invoke Application”.
@WebServlet(“/teste-mensagem”)
public class TesteMensagemServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest requisicao, HttpServletResponse
resposta)
throws ServletException, IOException {
PrintWriter saida = resposta.getWriter();
saida.println(“<html><body><h1> Teste Msg </h1></body></html>”);
}
}
Exemplo
135
Process Validations
Nesta etapa, os componentes com dados transmitidos pelos usuários por meio de
formulários de entrada sofrem as conversões e validações necessárias. Se não hou-
ve erro durante os processos de validação e conversão, o fluxo continua na etapa
“Update Model Values”. Caso aconteça algum erro, serão acrescentadas mensagens
de falha no contexto da requisição, e o fluxo sofrerá direcionamento para a etapa
“Render Response”.
Update Model Values
Nesta etapa, os valores convertidos e validados na fase “Process Validations” para
cada componente da árvore são guardados nas propriedades dos “Managed Beans”
definidos pela aplicação Web.
Invoke Application
Nesta fase, as tarefas relacionadas ao evento de usuário que iniciou a requisição po-
dem ser executadas porque as informações dos componentes já estão convertidas, va-
lidadas e armazenadas nos “Managed Beans” do modelo. Na fase “Invoke Application”,
o método que realiza a lógica do negócio apresenta a próxima tela ao usuário.
Render Response
Nesta etapa, além de ser criada e transmitida ao navegador do usuário, a próxima tela
do aplicativo deve ser armazenada para utilização na próxima requisição na etapa
“Restore View”.
Durante o processamento de uma requisição, a classe “Faces Servlet” pode utilizar os ob-
jetos “Managed Beans” para retornar as informações necessárias para construir a árvore
de componentes das páginas nas fases “Render Response” ou “Restore View”. As telas da
aplicação são definidas na camada “Visão”, que é acessada pela classe “Faces Servlet”
sempre que se precisam construir uma tela e sua respectiva árvore de componentes.
A classe “Faces Servlet” também pode executar um método no objeto “Managed
Beans” que processe a regra de negócio da requisição atual na fase “Invoke Application”.
Além disso, na fase “Update Model”, as informações já convertidas e validadas são salvas
nos objetos “Managed Beans” pela classe “Faces Servlet”.
136
As regras de negócio são realizadas na camada “Modelo”, a qual também gerencia as in-
formações da aplicação. Os objetos “Managed Beans” acionam a camada “Modelo” para
rodar alguma regra de negócio, recuperar ou mesmo alterar as informações gerenciadas
por essa camada.
A estrutura completa de uma aplicação JSF pode ser vista na figura a seguir.
Figura: Diagrama geral de uma aplicação JSF.
Fonte: Adaptada de //github.com/K19/K19-Cursos/tree/master/k12-desenvolvimento-web-com-jsf
Restore
View
Apply Request
Values
Requisição
HTTP
Process
Validation
Update
Model
Invoke
Application
Render
Response
Faces Servlet
MANAGED BEANS
CONTROLE
MODELO
ENTIDADES
REPOSITÓRIOS
MODELO
TELAS
TEMPLATES
TELAS PARCIAIS
Resposta
HTTP
137
NAVEGAÇÃO ENTRE TELAS DE UMA APLICAÇÃO WEB
As ferramentas de navegação JFS entre as telasde uma aplicação Web possibilitam
diversas formas de transição a partir de uma ação do usuário.
Uma das formas de navegação entre telas é a navegação implícita, que ocorre quando o
usuário aciona um objeto na tela (por meio de um botão de comando ou link) sinalizando
ao JSF por uma “string”, a qual será usada para a definição da próxima tela a ser exibida.
Essa “string” representa um sinal para a navegação denominado “outcome”, usado para
redirecionar para uma tela definida pelo arquivo xhtml correspondente.
Quando os componentes <h:commandButton> e <h:commandLink> são usados para de-
finir os objetos a serem acionados, é necessário especificar os sinais de navegação (“out-
comes”) usando o atributo “action”. Se forem usados os componentes <h:button> e <h:link>,
será necessário especificar os sinais de navegação pelo próprio atributo “outcome”.
Já no modo de navegação explícita, é possível relacionar um sinal de navegação
“outcome” a um arquivo de resposta, de forma independente das informações de nome
e endereço. Para configurar o JSF, esse relacionamento precisa ser registrado no arquivo
“faces-config.xml”, acrescentando as seguintes informações:
• Endereço do arquivo da tela de origem.
• Tela de resposta.
• Sinal de navegação “outcome”.
Para definir a página de resposta, o JSF verifica se há compatibilidade do
sinal de navegação “outcome” com alguma regra configurada no arquivo
faces-config.xml. Se houver compatibilidade, o JSF processará e executará
uma navegação explícita. Caso negativo, será procurado um arquivo compatí-
vel com o sinal de navegação “outcome” para realizar uma navegação implícita.
Por fim, a tela atual será mantida caso o arquivo não seja encontrado.
Importante
138
Na navegação estática, os sinais de navegação “outcome” estão definidos nos objetos
da interface acionados pelos usuários (botões de comando e links), gerando sempre o
mesmo endereço de navegação quando os objetos são acionados.
Para alterar para uma navegação dinâmica, é necessário que os objetos da interfa-
ce (botões de comando e links) sejam relacionados a métodos de ação dos objetos
“Managed Beans” usando a “Expression Language”. Ao adicionar a tag “<from-action>” à
regra de navegação, a página de resposta de uma navegação dinâmica também poderá
depender do método de ação que produziu o sinal “outcome”.
139
Componentes básicos, validação de campos,
PrimeFaces e Ajax JSF
A tecnologia JavaServer Faces – JSF oferece um conjunto básico de componentes para
a construção rápida e fácil de interfaces de usuário para aplicativos Web. Essa tecnolo-
gia permite a extensão de componentes-padrão para aprimorar suas funcionalidades ou
mesmo para criar componentes personalizados. Os componentes JSF podem ser asso-
ciados a objetos correspondentes no modelo de dados de um aplicativo. O JSF também
oferece suporte a componentes de interface de usuário com interfaces auxiliares para
validação e conversão, tratamento de eventos de usuário, entre outros.
Componentes básicos
Os componentes visuais básicos que formam as telas são acrescentados utilizando tags
nos arquivos XHTML, conforme definido na especificação do JSF. Essa especificação
define uma grande quantidade de tags, classificadas em diferentes bibliotecas, como:
• Core (http://java.sun.com/jsf/core).
• HTML (http://java.sun.com/jsf/html).
• Facelets (http://java.sun.com/jsf/facelets).
A estrutura básica de uma página JSF é muito similar a uma página HTML padrão, como
podemos observar a seguir.
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns = “http://www.w3.org/1999/xhtml”
xmlns:ui = “http://java.sun.com/jsf/facelets”
xmlns:h = “http://java.sun.com/jsf/html”
xmlns:f = “http://java.sun.com/jsf/core”>
<h:head>
<title> UVA Teste </title>
</h:head>
140
<h:body>
<h:outputText value = “Exemplo da página JSF”/>
</h:body>
</html>
Como em uma página HTML, o conteúdo JSF é especificado dentro da tag <html>, sendo
separado em um bloco de cabeçalho (“header”) pela tag <h:head> e o corpo (“body”) da
página, definido pela tag <h:body>. É importante notar a inclusão do prefixo “h:” no nome
das tags padrão do JSF. Outro ponto importante é a necessidade de importação das bi-
bliotecas de tags JSF por meio do pseudoatributo “xmlns” incluído na tag padrão <html>.
Essas bibliotecas são usadas na construção da página JSF.
É necessário comunicar aos navegadores Web a versão HTML utilizada na definição da
página por meio da declaração <!DOCTYPE>.
Para a execução adequada das páginas HTML criadas pelo JSF, é necessário usar as
tags <h:head> e <h:body>. Na fase “Render Response”, o JSF executa essas tags para
acrescentar funcionalidades de scritps e arquivos de estilo na página HTML transmitida
ao cliente.
Elementos de um formulário JSF
Um formulário JSF é especificado pela tag <h:form>, sendo formado geralmente por ob-
jetos gráficos do tipo:
• Caixas de texto (“text box”).
• Rótulos (“labels”).
• Botões (“buttons”).
• Caixas de seleção (“selection box”).
• Links.
A tag <h:form> cria um formulário HTML ao ser processada. O método “POST” do HTTP
normalmente é configurado para o formulário gerado.
Agora, vejamos com mais detalhes cada tipo de objeto gráfico.
• Caixas de texto
A ferramenta JSF especifica as seguintes formas de caixas de texto para a entrada de
dados pelo usuário:
141
• <h:inputText> possibilita a entrada de uma linha de caracteres.
• <h:inputTextarea> possibilita a entrada de várias linhas de caracteres.
• <h:inputSecret> possibilita a entrada de uma linha de caracteres sem mostrar
o conteúdo.
Usando o atributo “value”, um componente “caixa de texto” pode ser relacionado a uma
propriedade de um objeto “Managed Bean”. No momento em que o JSF cria a janela a
ser transmitida ao usuário, o valor da propriedade é retornado por um método “getter()”
padrão e armazena esse dado na respectiva caixa de texto. Por outro lado, na execução
de uma requisição, o JSF retira o dado na caixa de texto e utiliza o método “setter()” para
guardar o dado de valor na respectiva propriedade do objeto “Managed Bean”.
• Rótulos (“labels”)
Quando é necessário passar alguma informação ou instrução ao usuário, pode-se usar
um rótulo (ou “label”) na página Web por meio do componente <h:outputLabel>. O conteú-
do do rótulo é especificado pelo “value” e pode ser relacionado a um objeto caixa de texto
pelo atributo “for”, cujo valor deve ser idêntico ao atributo “id” da caixa de texto.
• Campos ocultos (“hidden fields”)
No momento em que um formulário é transmitido ao servidor Web, é possível acrescen-
tar informações automáticas por meio do componente <h:inputHidden>, o qual dispõe de
dois atributos: “value” e “id”. Também é possível relacionar um campo oculto a um objeto
“Managed Bean”.
• Caixas de seleção (“check box”)
Na ferramenta JSF, existem várias formas de objetos “check box” que podem ser asso-
ciados a um “Managed Bean” usando o atributo “value”. A seguir, serão mostrados alguns
tipos de caixas de seleção JSF:
<h:selectBooleanCheckbox>
Possibilita a escolha de opções binárias do tipo “sim”
(“true”) ou “não” (“false”).
<h:selectOneRadio>
Possibilita apenas uma escolha dentre vários “radio
buttons”.
<h:selectOneMenu>
Possibilita apenas uma escolha dentre as opções
de “menu”.
142
<h:selectOneListbox> Possibilita apenas uma escolha em uma listagem.
<h:selectManyCheckbox> Possibilita a escolha de múltiplas opções binárias.
<h:selectManyListbox>
Permite a escolha de múltiplas opções em uma
listagem.
<h:selectManyMenu>
Permite a escolha de múltiplas opções em um “menu”
de listagem.
• Botões de comando e links
Na ferramenta JSF, existem várias formas de botões de comando e links. Vejamos
alguns deles:
1 <h:commandButton> e <h:commandLink>
Estes componentes utilizam o método “POST” do HTTP para transmitir as informa-
ções de um formulário HTML ao servidorWeb.
2 <h:button> e <h:link>
É possível relacionar estes objetos a um “Managed Bean” usando “action” e
“actionListener”. Esses componentes implementam requisições GET do HTTP. Os
endereços URL das requisições são criados pela ferramenta JSF usando o atributo
“outcome”.
3 <h:outputLink>
Implementa requisições GET do HTTP com o endereço da URL sendo definido de for-
ma explícita no atributo “value”.
Textos e imagens
É possível utilizar o componente <h:outputText> a fim de adicionar textos em uma
página JSF por meio do atributo “value”. Em especial, pode-se usar o componente
<h:outputFormat> para apresentar textos com vários tipos de formatação. Para acrescen-
tar arquivos de imagem nas páginas JSF, é possível usar o componente <h:graphicImage>
juntamente com os atributos “value” ou “url” para informar o caminho do arquivo.
143
JavaScript e CSS
Utilizando tags padrão do HTML do tipo <script> e <link>, é possível acrescentar fun-
cionalidades de estilo (arquivos CSS) e scripts nas páginas da aplicação. Além disso,
o JSF permite adicionar recursos de forma alternativa com as tags <h:outputScript>
e <h:outputStylesheet>, que simplificam a elaboração de componentes visuais
customizados.
Validação de campos em páginas JSF
Como uma requisição no protocolo HTTP transmite as informações em formato texto
para a aplicação, geralmente é necessário modificar esse formato para algum outro tipo
de dado da linguagem Java Além disso, muitas vezes é preciso confirmar se os valores
foram convertidos de forma adequada de acordo com as regras de negócio da aplicação.
Para validar um campo para a entrada do ano de nascimento de um usuário, pode-se
implementar duas etapas, a saber:
1ª etapa: como as informações digitadas pelo usuário entram como uma “string”
de texto, será necessário modificar na aplicação para algum tipo de dado numérico
padrão Java.
2ª etapa: após essa conversão, será preciso definir uma etapa de validação adicional
para verificar se o valor do ano de nascimento poderá ser considerado real ou não.
Contamos com alguns validadores-padrão definidos no JSF. São eles:
Para saber mais informações sobre outros componentes gráficos referentes
às bibliotecas JSF, visite a página Java Community Process, disponível em:
http://www.jcp.org/en/jsr/detail?id=314.
Ampliando o foco
http://www.jcp.org/en/jsr/detail?id=314
144
<f:validateRequired>
Checa se o preenchimento de um campo de dados é obri-
gatório e deve ser implementado pelo atributo “required”
do objeto ou usando-se a tag <f:validateRequired>.
<f:validateLongRange>
Verifica se um valor inteiro está dentro de um intervalo de
números.
<f:validateDoubleRange>
Verifica se um valor real está dentro de um intervalo de
números.
<f:validateLength>
Confirma se a quantidade de caracteres está de acordo
com o comprimento máximo ou mínimo da “string”.
<f:validateRegex>
É utilizada para confirmar se uma certa expressão ou res-
trição está sendo atendida pelo texto digitado.
PrimeFaces — biblioteca de componentes visuais
A PrimeFaces é uma das principais bibliotecas do JavaServer Faces para objetos visuais.
É necessário copiar o arquivo JAR referente à biblioteca PrimeFaces no diretório da apli-
cação JSF para ter acesso às funcionalidades e aos componentes visuais. Além disso,
para acessar os objetos da PrimeFaces nas páginas da aplicação, é necessário acres-
centar o “namespace” adequado.
Vejamos alguns exemplos de componentes visuais da biblioteca PrimeFaces.
• AutoComplete: com este objeto, as aplicações podem dar sugestões para com-
pletar os campos de forma automática.
• Calendar: objeto com interface para a escolha de data em um calendário.
• Chart: objeto utilizado para a criação de gráficos em aplicações JSF.
• DataTable: objeto usado para exibir informações em forma de tabelas com fun-
cionalidades de filtros.
• DataExporter: este componente possibilita a exportação de dados em vários for-
matos de arquivos, tais como: texto simples (csv, xml), planilha Excel (xls) e Portable
Document Format (PDF).
• InputMask: em objetos gráficos tipo “caixa de texto”, este componente especifica
máscaras para auxiliar o preenchimento de dados.
• MegaMenu: este objeto é usado para a elaboração de “menus” em aplicações JSF.
• PickList: permite a seleção de objetos de um conjunto.
• Poll: por meio deste componente, as requisições Ajax são executadas de
forma periódica.
145
Ajax JSF
Utilizando o conceito Ajax (Asynchronous Javascript and XML), é possível atualizar ape-
nas partes de uma página, e não todo o seu conteúdo. Outra funcionalidade importan-
te é executar requisições mantendo a navegação dos usuários. Essas funções do Ajax
ajudam a melhorar a interatividade entre os usuários e as aplicações Web, em especial
quando existem telas com alta complexidade e com muita informação a ser exibida.
Fazendo requisições Ajax
Quando acontecem eventos associados aos objetos visuais das páginas, são executadas
as requisições Ajax. Para indicar os componentes e os eventos que devem implementar
requisições Ajax, é necessário usar a tag <f:ajax>.
No exemplo a seguir, sempre que o conteúdo da caixa de texto for alterado, uma re-
quisição Ajax será executada, considerando que o evento “onchange” é o padrão para
o componente acionado. É possível alterar o evento que será usado para disparo das
requisições Ajax usando o atributo “event” da tag <f:ajax> caso seja compatível com o
componente a ser acionado.
É possível agrupar diferentes componentes usando a tag <f:ajax> a fim de imple-
mentar as funcionalidades do Ajax. Nesse caso, o JSF também considera o evento-
-padrão de cada componente agrupado. O evento “onchange” também é padrão do
componente <h:inputSecret>, enquanto o evento “onclick” é padrão do componente
<h:commandButton>. Caso seja usado um grupo de componentes, será possível utilizar
o atributo “event” para definir o evento de disparo das requisições Ajax.
<h:inputText>
<f:ajax/>
</h:inputText>
Exemplo
146
Processamento de apenas uma parte da tela
É possível definir os componentes de tela que precisam ser processados pela ferramenta
JavaServer Faces quando for realizada uma requisição Ajax. Caso seja transmitido um
formulário, muitas vezes será preciso verificar apenas os objetos desse formulário es-
pecífico. Utilizando o atributo “execute” do <f:ajax>, é possível especificar os objetos ou
componentes que precisam ser processados. Se um componente for processado, seus
objetos internos também o serão.
No exemplo a seguir, no momento em que o botão “Salvar” for acionado, uma requisição
Ajax será executada no servidor, e o formulário de dados e seus componentes internos
serão processados.
<f:ajax>
<h:inputText/>
<h:inputSecret/>
<h:commandButton value =“OK”/>
</f:ajax>
Exemplo
<h:form id = “dados_form”>
<h:inputText/>
<h:inputSecret/>
<h:commandButton value = “Salvar”>
<f:ajax event = “click” execute = “dados_form” />
</h:commandButton >
</h:form >
Exemplo
147
Outras funcionalidades Ajax
Para gerar uma recarga parcial da tela, no momento do retorno no navegador de uma re-
quisição Ajax, é possível especificar os objetos que devem sofrer atualização por meio do
atributo “render” da tag <f:ajax>. Esse atributo deve conter uma lista dos identificadores
dos objetos que necessitam de atualização.
Também é possível relacionar uma requisição Ajax a um método usando o atributo
“listener” da tag <f:ajax>. A execução do método ocorrerá quando o servidor processar
essa requisição na fase “Invoke Application”.
Além disso, utilizando algumas palavras-chave relacionadas a conjuntos específicos de
objetos, é possível passar listas de objetos para os atributos “execute” e “render”, como
mostrado a seguir:
• “@this” se for necessário passar o objeto que gerou a requisição Ajax.
• “@all” se for necessário passar todos os objetos da tela.
• “@none” se não for necessário passar nenhum objeto.
148
Para ampliar o seu conhecimentoveja o material complementar da Unidade 4,
disponível na midiateca.
MIDIATECA
Durante o desenvolvimento de uma aplicação Web, muitas vezes é necessário
controlar as informações digitadas pelo usuário em uma página de entrada, im-
plementar alguma operação de transformação dessa informação e apresentar
o resultado em uma página de resposta.
Como apresentado nesta unidade, os “Managed Beans” funcionam como
modelo para o componente da interface do usuário e podem ser aces-
sados na página JSF. Eles podem ser registrados usando-se a anotação
“@ManagedBean”, tornando mais fácil o gerenciamento de requisições e a na-
vegação na aplicação Web.
No exemplo a seguir, apresentaremos a criação de um objeto “Managed Bean”
declarado com a anotação @ManagedBean. O atributo “mensagem” armaze-
nará uma “string” de texto transmitida pelo usuário, e o método “retiraEspaco()”
alterará a mensagem removendo os espaços em branco iniciais e finais, re-
tornando uma “string” para informar a próxima tela a ser transmitida para
o usuário. Por fim, o método “setMensagem()” será usado pela interface
“Faces Servlet” para guardar o texto transmitido no objeto “Managed Bean”, e o
método “getMensagem()”, para retornar e exibir a mensagem a seguir.
import javax.faces.bean.ManagedBean;
@ManagedBean
public class MsgManagedBean {
private String mensagem
NA PRÁTICA
149
public String retiraEspaco() {
this.mensagem = this.mensagem.trim() ;
return “tela_retorno”;
}
public String getMensagem() {
return this.mensagem;
}
public void setMensagem(String mensagem) {
this.mensagem = mensagem;
}
}
Após a implementação do “Managed Bean”, a mensagem do usuário é enviada
para um formulário em uma página Web:
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns = “http://www.w3.org/1999/xhtml”
xmlns:ui = “http://java.sun.com/jsf/facelets”
xmlns:h = “http://java.sun.com/jsf/html”
xmlns:f = “http://java.sun.com/jsf/core”>
<h:head>
<title>UVA Teste</title>
</h:head>
<h:body>
<h:form>
<h:outputLabel value = “Mensagem:”/ >
<h:inputTextarea value = “#{msgManagedBean.texto}”/ >
<h:commandButton value = “Formatar” action = “#{msgManagedBean.reti-
raEspaco}”/>
</h:form>
</h:body>
</html>
Para associar o objeto “Managed Bean” e a página Web, o campo texto
do objeto “caixa de entrada” foi relacionado à propriedade “mensagem” do
MsgManagedBean, e o objeto “botão de comando” foi relacionado ao método
“retiraEspaco()” do MsgManagedBean.
150
Para apresentar a mensagem de texto sem os espaços em branco, é possível
implementar uma página de resposta, mostrada a seguir. Nela, a caixa de saída
de texto está relacionada à propriedade “mensagem” do MsgManagedBean.
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns = “http://www.w3.org/1999/xhtml”
xmlns:ui = “http://java.sun.com/jsf/facelets”
xmlns:h = “http://java.sun.com/jsf/html”
xmlns:f = “http://java.sun.com/jsf/core”>
<h:head>
<title>UVA Teste</title>
</h:head>
<h:body>
<h:outputText value =“#{msgManagedBean.mensagem}”/>
</h:body>
</html>
151
Resumo da Unidade 4
Nesta unidade vimos como utilizar o JavaServer Faces – JSF para o desenvolvimento da
camada de visão para aplicações Web em linguagem Java. Também foram abordadas
técnicas de configuração do JavaServer Faces – JSF e como usar os componentes vi-
suais básicos do JSF.
Além disso, foram apreendidas as técnicas de utilização dos “Managed Beans” para a
implementação de requisições e a navegação na aplicação Web. Por fim, foram aborda-
dos os conceitos da biblioteca PrimeFaces e do Ajax JSF para o desenvolvimento das
aplicações Web.
152
Referências
DEITEL, P. Java: como programar. 10. ed. São Paulo: Pearson Education do Brasil, 2017.
Biblioteca Virtual.
GAMMA, E. et al. Padrões de projeto: soluções reutilizáveis de software orientado a ob-
jetos. Porto Alegre: Bookman, 2000. xii, 364 p.
MANZANO, J. A. N. G.; COSTA JÚNIOR, R. A. Java 7: programação de computadores:
guia prático de introdução, orientação e desenvolvimento. São Paulo: Érica, 2011. Minha
Biblioteca.
ORACLE CORPORATION. JSR 314: JavaServer Faces 2.0. Java Community Process,
2020. Disponível em: http://www.jcp.org/en/jsr/detail?id=314. Acesso em: 28 set. 2020.
ORACLE CORPORATION. JSR 366: Java Platform, Enterprise Edition 8 (Java EE 8)
Specification. Java Community Process, 2020. Disponível em: https://jcp.org/en/jsr/de-
tail?id=366. Acesso em: 27 set. 2020.
http://www.jcp.org/en/jsr/detail?id=314. Acesso em: 28 set. 2020
https://jcp.org/en/jsr/detail?id=366
https://jcp.org/en/jsr/detail?id=366