Olá,
Hoje vamos mostrar como subir uma pequena aplicação no apache tomcat 7 que faça uso de JPA pra facilitar as manipulações do banco de dados e jstl para facilitar a apresentação de dados no jsp.
- Fedora 17 64 bits
- OpenJDK 1.7
- Apache Tomcat 7.0.27
- eclipse indigo SR2 (3.7.2)
- H2 database 1.3
- EclipseLink 2.3.1
Como de costume, vamos proceder com os downloads. Para instalar o JDK, entre num terminal o seguinte comando:
sudo yum install java-1.7.0-openjdk-devel
Faça o download do tomcat direto da fonte, preferencialmente a versão .tar.gz:
O download do tomcat também providenciará o suporte a jstl, que veremos como ajustar mais à frente.
Faça o download do eclipse em sua versão JEE:
O banco de dados H2 você baixa a versão zip:
O EclipseLink você baixa o "Installer Zip":
Uma vez coletados os ingredientes, vamos proceder com a montagem do ambiente. Entre na pasta Downloads e descompacte os arquivos que baixamos:
Entre na pasta do eclipse e execute-o. Deixe-o com o workspace padrão:
Uma vez carregado, o eclipse lhe apresentará uma tela de welcome. Livre-se dela e siga para o workbench:
Aí está, o workbench JEE do eclipse, velho conhecido seu, assim espero. Siga para a aba Servers para criarmos um novo servidor clicando no link "new server wizard":
No campo "Select the server type" escreva "tomcat v7" e selecione a opção que sobrar:
Aperte Next, e você verá a tela de configuração da runtime de servidor. Aperte browse e selecione a pasta do tomcat que descompactamos previamente:
Após pressionar finnish, você terá o tomcat disponível no seu workspace:
O passo seguinte é muito simples, vamos criar um novo projeto web chamado agenda. pressione o atalho disponível na barra do eclipse:
Pronto, temos um projeto Java web disponível, podemos começar a trabalhar de verdade, :-)
Nosso modelo de dados, de maneira geral, terá Contatos e Endereços. Nada mais. O único detalhe relevante é que um Contato poderá ter um ou mais Endereços.
Assim sendo, teremos duas telas de cadastro e duas telas de listagem, além de duas telas de status intermediárias que explicarei a seguir.
Vamos começar com as classes de modelo. Crie uma classe chamada Endereco no pacote exemplo.model:
A classe Contato você faz seguindo os passos já mostrados acima. Vamos adicionar alguns atributos ao nosso modelo de dados, a começar pelo Endereço:
package exemplo.model;
public class Endereco {
private long id;
private String rua;
private int numero;
private String complemento;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getRua() {
return rua;
}
public void setRua(String rua) {
this.rua = rua;
}
public int getNumero() {
return numero;
}
public void setNumero(int numero) {
this.numero = numero;
}
public String getComplemento() {
return complemento;
}
public void setComplemento(String complemento) {
this.complemento = complemento;
}
}
A classe Contato ficará assim:
package exemplo.model;
import java.util.List;
public class Contato {
private long id;
private String nome;
private String telefone;
private List enderecos;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getTelefone() {
return telefone;
}
public void setTelefone(String telefone) {
this.telefone = telefone;
}
public List getEnderecos() {
return enderecos;
}
public void setEnderecos(List enderecos) {
this.enderecos = enderecos;
}
}
Uma vez criado o modelo, hora de escrever telas que o representem. Vamos criar a tela de cadastro de Contato:
Este JSP é simples, teremos um formulário bem básico, nada complicado de entender:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head></body> </html>Salvar Contato </head> <body>
Você já pode até ver como ele fica no navegador, com ele aberto basta apertar o "play":
Atenção para o atributo action da tag form: ela tem o nome da ação que este formulário tentará executar. Se você apertou o botão Salvar e um erro 404 apareceu, não se preocupe, vamos corrigir isso agora. De volta ao eclipse, vamos criar um servlet:
Após apertar Next, um passo importante: mude o valor do URL Mappings para "/vaisalvar", que é o que tem no form. Só então pressione finnish:
O servlet basicamente mapeia os verbos HTTP para métodos java. Como não informamos o atributo method na tag form, o verbo aqui será o GET. Assim sendo, vamos modificar este servlet que aí está para que ele fique deste jeito:
package exemplo.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import exemplo.model.Contato;
@WebServlet("/vaisalvar")
public class Salvar extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
long id = 0L;
String sId = request.getParameter("id").trim();
String sNome = request.getParameter("nome");
String sTelefone = request.getParameter("telefone");
if (!"".equals(sId))
id = Long.parseLong(sId);
Contato c = new Contato();
c.setId(id);
c.setNome(sNome);
c.setTelefone(sTelefone);
// TODO mais modificações a seguir
// devemos ainda redirecionar para a tela de resultado
request.getRequestDispatcher("/sucesso.jsp").forward(request, response);
}
}
Já podemos ver parte do trabalho caminhando: temos a recuperação dos dados do form através de chamadas "request.getParameter", verificação de id válida (que será útil para salvar um novo ou atualizar), e redirecionamento para uma página intermediária de status. Página essa, que você deve criar no projeto, de forma parecida com o que foi feito na criação do jsp de contato.
Poderíamos fazer o fowrard para o mesmo jsp de origem, entretanto ao usar uma página intermediária você não precisará prever a limpeza do formulário após a submissão. É um problema antigo, típico de web 1.0, e que muitas pessoas esquecem como combater.
Neste momento, precisamos retornar às configurações para habilitarmos o suporte do H2 e do JPA neste projeto. Retorne à pasta Downloads e entre na pasta descompactada do H2; vamos copiar a biblioteca do banco para as bibliotecas do tomcat:
O h2 deverá então ser listado como uma das bibliotecas do tomcat dentro do projeto eclipse:
Como visto em tutoriais anteriores, com JEE a forma correta de configurar o acesso ao banco de dados é via DataSource. Este nós vamos configurar no contexto global do servidor.
Entre no projeto Servers, na pasta de configuração do tomcat 7 e abra o arquivo server.xml, procure a seção GlobalNamingResources:
Adicione nesta seção o seguinte DataSource:
Deve ficar assim:
Agora siga para a pasta do projeto, vamos para WebContent>META-INF. Lá crie um arquivo chamado context.xml com o seguinte conteúdo:
Isso dá à aplicação o direito de usar o DataSource.
Caso fôssemos usar apenas JDBC, nosso trabalho de configuração de banco de dados estaria terminado. Mas agora vamos aos ajustes para usarmos JPA neste projeto. Siga para a pasta onde descompactamos o EclipseLink e copie os jars eclipselink.jar e javax.persistence_2.0.3.v201010191057.jar para a lib do tomcat:
Agora voltamos ao projeto, mas desta vez na pasta src: crie uma pasta chamada META-INF lá:
Crie então dentro desta pasta o arquivo persistence.xml:
java:comp/env/jdbc/agenda-ds exemplo.model.Contato exemplo.model.Endereco
Nas classes Contato e Endereco faça as seguintes modificações:
package exemplo.model;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
@Entity
public class Contato {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
private String nome;
private String telefone;
@OneToMany
private List enderecos;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getTelefone() {
return telefone;
}
public void setTelefone(String telefone) {
this.telefone = telefone;
}
public List getEnderecos() {
return enderecos;
}
public void setEnderecos(List enderecos) {
this.enderecos = enderecos;
}
}
package exemplo.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
@Entity
public class Endereco {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
private String rua;
private int numero;
private String complemento;
@ManyToOne
@JoinColumn(name = "CONTATO_ID", referencedColumnName = "ID")
private Contato contato;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getRua() {
return rua;
}
public void setRua(String rua) {
this.rua = rua;
}
public int getNumero() {
return numero;
}
public void setNumero(int numero) {
this.numero = numero;
}
public String getComplemento() {
return complemento;
}
public void setComplemento(String complemento) {
this.complemento = complemento;
}
public Contato getContato() {
return contato;
}
public void setContato(Contato contato) {
this.contato = contato;
}
}
Como estamos no tomcat e não num AppServer completo, vamos criar um enum utilitário para recuperarmos uma fererência ao EntityManager:
package exemplo.util;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public enum JPAUtil {
INSTANCE;
private EntityManagerFactory emf;
JPAUtil() {
emf = Persistence.createEntityManagerFactory("agenda-pu");
}
public EntityManager getEntityManager() {
return emf.createEntityManager();
}
}
Usaremos ele no servlet Salvar:
package exemplo.controller;
import java.io.IOException;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import exemplo.model.Contato;
import exemplo.util.JPAUtil;
@WebServlet("/vaisalvar")
public class Salvar extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
EntityManager em = JPAUtil.INSTANCE.getEntityManager();
String sId = request.getParameter("id").trim();
String sNome = request.getParameter("nome");
String sTelefone = request.getParameter("telefone");
Contato c = null;
if (!"".equals(sId))
c = em.find(Contato.class, Long.parseLong(sId));
else
c = new Contato();
c.setNome(sNome);
c.setTelefone(sTelefone);
EntityTransaction t = em.getTransaction();
t.begin();
em.persist(c);
t.commit();
// devemos ainda redirecionar para a tela de resultado
request.getRequestDispatcher("/sucesso.jsp").forward(request, response);
}
}
Siga para o sucesso.jsp e adicione o seguinte código a seguir. Ele serve apenas para desviar o usuário da tela atual. Aproveite e crie logo o index.jsp que vai a seguir:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head>Sucesso! </head> <body>Sucesso!
<script type="text/javascript"> setTimeout(function() { window.location.href = 'index.jsp'; }, 3000); </script> </body> </html>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head>Agenda </head> <body> Novo Contato
Listar Contatos </body> </html>
Criaremos agora o servlet de listagem de contatos. Faça do mesmo jeito que você fez com o servlet anterior, e mude o conteúdo dele para o que vem abaixo:
package exemplo.controller;
import java.io.IOException;
import java.util.List;
import javax.persistence.EntityManager;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import exemplo.model.Contato;
import exemplo.util.JPAUtil;
/**
* Servlet implementation class Listar
*/
@WebServlet("/vailistar")
public class Listar extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
EntityManager em = JPAUtil.INSTANCE.getEntityManager();
List contatos = //
em.createQuery("select c from Contato c", Contato.class)//
.getResultList();
request.setAttribute("contatos", contatos);
request.getRequestDispatcher("/listar_contatos.jsp")//
.forward(request, response);
}
}
O workspace deve estar ficando assim:
Observe que desta vez o redirecionamento é para listar_contatos.jsp e não para o sucesso.jsp; Em se tratando de Aplicações Action-Based, no caso de listagens devemos navegar para o controlador primeiro e só então irmos para a apresentação.
Este jsp ainda não existe, então vá e crie-o com o seguinte conteúdo:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%--declaração da taglib --%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head>Listar Contatos </head> <body> </body> </html>
Aqui um ponto interessante: este jsp deve surgir para você com um erro na liha de declaração da taglib. Deve estar mais ou menos assim:
A correção deste problema é muito simples, siga para a pasta do tomcat, entre em webapps>examples>WEB-INF/lib:
Copie e cole os dois jars ali presentes para a pasta WebContent>WEB-INF>lib do projeto:
Feito isso, um simples project>clean deve sumir com o alerta de erro:
Dando continuidade ao jsp, modifique-o para ficar assim:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%--declaração da taglib --%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head>Listar Contatos </head> <body>
| Id | Nome | Telefone | Endereços |
|---|---|---|---|
| ${contato.id} | ${contato.nome} | ${contato.telefone} | Ver | Novo |
Com jstl fica muito melhor manipular os dados que o servidor encaminha para o jsp.
Observe que criaremos dois novos servlets, um para listar endereços e outro para salvar endereços, além dos jsp's com o formulário para receber os dados de endereço e o que recebe a listagem. Primeiro os arquivos do fluxo de listagem:
package exemplo.controller;
import java.io.IOException;
import java.util.List;
import javax.persistence.EntityManager;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import exemplo.model.Endereco;
import exemplo.util.JPAUtil;
/**
* Servlet implementation class ListarEndereco
*/
@WebServlet("/vailistarendereco")
public class ListarEndereco extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String sId = request.getParameter("contatoid");
long idContato = Long.parseLong(sId);
List enderecos = null;
EntityManager em = JPAUtil.INSTANCE.getEntityManager();
String q = "select e from Endereco e where e.contato.id = :id";
enderecos = em.createQuery(q, Endereco.class)//
.setParameter("id", idContato).getResultList();
request.setAttribute("enderecos", enderecos);
request.getRequestDispatcher("/listar_enderecos.jsp")//
.forward(request, response);
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head>Listar Endreços </head> <body>Listar Endreços do Contato ${param.contatoid}
| Id | Rua | Número | Complemento |
|---|---|---|---|
| ${e.id} | ${e.rua} | ${e.numero} | ${e.complemento} |
Agora o fluxo de salvamento:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head></body> </html>Salvar Endereços </head> <body>
package exemplo.controller;
import java.io.IOException;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import exemplo.model.Contato;
import exemplo.model.Endereco;
import exemplo.util.JPAUtil;
/**
* Servlet implementation class SalvarEndereco
*/
@WebServlet("/vaisalvarendereco")
public class SalvarEndereco extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String sIdContato = request.getParameter("idcontato");
String sId = request.getParameter("id");
String rua = request.getParameter("rua");
String sNumero = request.getParameter("numero");
String complemento = request.getParameter("complemento");
EntityManager em = JPAUtil.INSTANCE.getEntityManager();
Endereco e = null;
if (!"".equals(sId))
e = em.find(Endereco.class, Long.parseLong(sId));
else {
e = new Endereco();
long contatoId = Long.parseLong(sIdContato);
Contato c = em.find(Contato.class, contatoId);
e.setContato(c);
}
e.setRua(rua);
e.setNumero(Integer.parseInt(sNumero));
e.setComplemento(complemento);
EntityTransaction tx = em.getTransaction();
tx.begin();
em.persist(e);
tx.commit();
request.getRequestDispatcher("/sucesso.jsp").forward(request, response);
}
}
Com isso você terá uma aplicação completamente funcional, simples de entender e que mostra o básico de um projeto web com JPA. Reinicie o tomcat e visite a aplicação em http://localhost:8080/agenda e tente salvar alguns contatos e adicionar endereços.
Detalhes relevantes mas que não mostrei aqui:
- Usar JPA integrado com JEE fullstack é muito mais simples do que mostrei aqui; não precisamos, por exemplo, baixar as libs do EclipseLink, pois normalmente o AppServer já tem uma biblioteca de propósito equivalente dentro de sua runtime básica.
- O trecho transacional, visto em todos os casos em que precisamos salvar entidades, é desnecessário caso usemos o EntityManager de dentro de um EJB.
- A propriedade especial que colocamos no persistence.xml não é padrão, caso a sua implementação de JPA seja o Hibernate, existe uma chave equivalente mas o nome é outro.
Espero que o tutorial lhe seja útil, hoje paramos por aqui.
Até mais.
Nenhum comentário :
Postar um comentário