Criando o Controlador de Produtos
8.1 - Listando produtos
Relembrando o que temos no nosso projeto, criamos o dao de produto para encapsular as chamadas ao Hibernate, e vimos como se usa o VRaptor 3, recebendo e disponibilizando valores da view. Precisamos agora integrar essas duas partes, para que possamos fazer o cadastro do produto pelo browser.
Vamos começar criando nossa classe que fará a lógica de negócios. O nome da classe
poderia ser qualquer nome válido, mas usaremos uma convenção do VRaptor: utilizar
o sufixo Controller
.
Vimos que o nome da classe é utilizado pelo VRaptor para registrar esse componente, para que
quando fizermos uma chamada do browser, essa classe seja encontrada e algum método
seja invocado. Mas utilizando o nome ProdutosController
, o VRaptor vai mapear essa classe para
apenas /produtos
, removendo o sufixo Controller
que faz parte da convenção.
Dentro dessa classe, teremos um método para listar todos os produto. Esse método será invocado pelo VRaptor e o produto em questão será passado com os dados já populados através dos dados do formulário.
Primeiro, vamos listar todos os produtos existentes no banco de dados. Para isso, vamos usar o método
createCriteria
de Session
que cria uma Criteria
. Através de um Criteria
, temos acesso
a diversas operações no banco de dados; uma delas é listar tudo com o método list()
.
Nosso método listaTudo()
no ProdutoDao
fica assim:
public class ProdutoDao { //... public List<Produto> listaTudo() { return this.session.createCriteria(Produto.class).list(); } }
Com essa listagem pronta, podemos usá-la no ProdutosController
, e criar uma lógica
que lista produtos. Coloque a classe no pacote br.com.caelum.goodbuy.controller
:
package br.com.caelum.goodbuy.controller; public class ProdutosController { public List<Produto> lista() { ProdutoDao dao = new ProdutoDao(); return dao.listaTudo(); } }
Olhando para esse método, que no início tínhamos definido para ser nossa regra de negócio, será que temos apenas isso? Será que apenas listamos os produtos?
Na verdade esse método está fazendo mais tarefas do que deveria. Ele possui algumas responsabilidades
extras, por exemplo criar o ProdutoDao. A única parte que é a regra de negócio para esse caso
é a chamada ao dao.listaTudo()
. O resto é apenas infraestrutura para permitir executar essa tarefa.
Avaliando esse problema, podemos perceber que estamos buscando recursos,
nesse caso um dao
, e portanto nos preocupando demasiadamente com os mesmos. Se buscar um recurso é ruim,
seja pela complexidade ou dificuldade no acesso, e também por não fazer parte da regra de negócio,
que tal se ao invés de criá-lo, recebêssemos o mesmo?
1 package br.com.caelum.goodbuy.controller; 2 3 // import's 4 5 @Resource 6 public class ProdutosController { 7 8 private ProdutoDao dao; 9 10 public ProdutosController(ProdutoDao dao) { 11 this.dao = dao; 12 } 13 14 public List<Produto> lista() { 15 return dao.listaTudo(); 16 } 17 18 }
Repare como seria mais simples nosso método que executa a regra de negócio, e também como o método só executa a parte que realmente lhe interessa. A busca dos recursos necessários para a execução de nessa lógica de negócios - nesse caso a criação do dao - não interessa pra essa classe.
Um ponto muito importante que temos que notar é que para que essa classe funcione corretamente, precisamos
de uma instância de ProdutoDao
. Se não tivermos essa instância, é impossível executar nossa regra de
negócios, pois não existe construtor que não receba um ProdutoDao
.
Como vimos antes, essa lógica redireciona para a jsp /WEB-INF/jsp/produtos/lista.jsp
, e
como retornamos uma lista de produtos, existe uma variável chamada ${produtoList}
disponível
no jsp.
Para podermos mostrar a listagem de um jeito mais fácil, vamos usar uma taglib da JSTL chamada
c:forEach
, que é capaz de iterar sobre uma lista passada.
<c:forEach items="${produtoList}" var="produto"> </c:forEach>
Usando essa taglib, vamos criar uma tabela com todos os produtos do sistema:
<table> <thead> <tr> <th>Nome</th> <th>Descrição</th> <th>Preço</th> </tr> </thead> <tbody> <c:forEach items="${produtoList}" var="produto"> <tr> <td>${produto.nome }</td> <td>${produto.descricao }</td> <td>${produto.preco }</td> </tr> </c:forEach> </tbody> </table>
8.2 - Quais são minhas dependências?
Tudo que um objeto necessita para permitir a execução de seus métodos, isto é, tudo o que ele depende, pode ser passado para o mesmo através de métodos (como os setters, por exemplo), ou do construtor.
Na nossa abordagem, utilizamos o construtor da classe ProdutosController
para receber nossas dependências e,
com isso, ganhamos a garantia de que, se tenho um objeto do tipo ProdutosController
na minha mão, posso invocar
o método adiciona
pois o atributo dao
teve seu valor atribuído durante a construção do objeto.
Ao adicionar o construtor customizado, perdemos o construtor padrão, e não podemos mais instanciar essa classe assim:
ProdutosController controller = new ProdutosController();
Essa característica de, com um objeto em mãos, saber que seus métodos podem ser invocados sem necessidade de nenhuma dependência externa extra, é parte do que foi chamado de Good Citizen: http://docs.codehaus.org/display/PICO/Good+Citizen
Em um objeto todas as dependências devem ser passadas durante o processo de construção evitando assim um possível estado incosistente.
Como poderia então criar o meu ProdutosController
?
1 ProdutoDao dao = new ProdutoDao(); 2 ProdutosController controller = new ProdutosController(dao);
Repare que logo após a segunda linha ser executada, tenho a confiança de que o objeto referenciado pela
variável controller
está preparado para ter seus métodos invocados.
Dessa maneira injetamos todas as nossas dependências durante a instanciação do controlador, um trabalho manual que ficará extremamente repetitivo e propício a erro a medida que aumenta o número de dependências de nossa classe.
8.3 - Injeção de Dependências
Sendo assim, fica muito mais fácil se fizermos uso de uma API que execute todo o processo de injeção automaticamente.
No nosso caso, quem vai instanciar essa classe? Nós ou o VRaptor? Essa classe será instanciada pelo VRaptor. Mas se o VRaptor fizer isso, como ele descobrirá sobre interligações entre as dependências?
Uma vez que precisamos da classe ProdutoDao
como dependência nossa, precisamos notificar o framework que o mesmo
se encarregará de gerenciar as instâncias de ProdutoDao
, isto é, nesse caso específico, ele
criará uma instância e utilizará como argumento para o construtor. Para fazer isso, basta anotarmos essa classe com
@Component
.
package br.com.caelum.goodbuy.dao; // import's import br.com.caelum.vraptor.ioc.Component; @Component public class ProdutoDao { //... }
Agora a classe ProdutoDao
também será controlada pelo VRaptor. Dessa forma, quando o VRaptor for
instanciar a classe ProdutosController
, ele verificará que ela depende de um ProdutoDao
,
e criará uma instância da mesma.
O que acabamos de fazer foi criar uma maneira de passar as dependências para onde for necessário, ou seja, um mecanismo de "injetar" as dependências. Por esse motivo, esse conceito é chamado de Injeção de Dependências.
O VRaptor está fortemente baseado nesse conceito, uma vez que até ele mesmo utiliza o mesmo para conectar seus componentes internos. O conceito básico por trás de Dependency Injection (DI) é que você não deve buscar aquilo que deseja acessar, mas tais necessidades devem ser fornecidas para você.
Isso se traduz, por exemplo, na passagem de componentes através do construtor de seus controladores. Imagine que seu controlador de clientes necessita acessar um dao. Sendo assim, especifique claramente essa necessidade.
Testando sua aplicação
Ao usarmos injeção de dependências, ganhamos uma característica muito boa na nossa aplicação: a testabilidade. Se recebemos nossas dependências no construtor, conseguimos passar implementações falsas ou controladas e, assim, testar unitariamente nossas classes.
Você pode encontrar um conteúdo mais aprofundado sobre testes nos cursos FJ-22 - Laboratório Java com Testes, JSF, Web Services e Design Patterns e PM-87 - Práticas ágeis de desenvolvimento de software.
8.4 - Exercícios
-
Abra a classe
ProdutoDao
e adicione o método para listar todos os produtos:public class ProdutoDao { //... public List<Produto> listaTudo() { return this.session.createCriteria(Produto.class).list(); } }
Crie a classe
ProdutosController
no pacotebr.com.caelum.goodbuy.controller
. Crie também o métodolista
, que retorna umaList<Produto>
.-
Anote a classe com a anotação
@Resource
.1 package br.com.caelum.goodbuy.controller; 2 3 // import's 4 5 import br.com.caelum.vraptor.Resource; 6 7 @Resource 8 public class ProdutosController { 9 10 public List<Produto> lista() { 11 } 12 13 }
-
Crie o construtor que recebe uma instância de
ProdutoDao
, e guarde essa instância em um atributo.1 package br.com.caelum.goodbuy.controller; 2 3 // import's 4 import br.com.caelum.vraptor.Resource; 5 6 @Resource 7 public class ProdutosController { 8 9 private final ProdutoDao dao; 10 11 public ProdutosController(ProdutoDao dao) { 12 this.dao = dao; 13 } 14 15 public List<Produto> lista() { 16 } 17 18 }
-
No método
lista
, faça a chamada ao métodolistaTudo
dodao
.1 package br.com.caelum.goodbuy.controller; 2 3 // import's 4 5 import br.com.caelum.vraptor.Resource; 6 7 @Resource 8 public class ProdutosController { 9 10 private final ProdutoDao dao; 11 12 public ProdutosController(ProdutoDao dao) { 13 this.dao = dao; 14 } 15 16 public List<Produto> lista() { 17 return dao.listaTudo(); 18 } 19 20 21 }
Na pasta
WEB-INF/jsp
, crie a pastaprodutos
.-
Crie o arquivo
lista.jsp
na pasta/WEB-INF/jsp/produtos
.<table> <thead> <tr> <th>Nome</th> <th>Descrição</th> <th>Preço</th> </tr> </thead> <tbody> <c:forEach items="${produtoList}" var="produto"> <tr> <td>${produto.nome }</td> <td>${produto.descricao }</td> <td>${produto.preco }</td> </tr> </c:forEach> </tbody> </table>
-
Acesse a URI que executa o método lista:
http://localhost:8080/goodbuy/produtos/lista
. -
Deu uma exception! A informação mais importante dela está no final da Stacktrace:
... Caused by: org.springframework.beans.factory .NoSuchBeanDefinitionException: No unique bean of type [br.com.caelum.goodbuy.dao.ProdutoDao] is defined: Unsatisfied dependency of type [class br.com.caelum.goodbuy.dao.ProdutoDao]: expected at least 1 matching bean at org.springframework.beans.factory.support....
Ou seja, o Spring -- que é usado pelo VRaptor para gerenciar dependências -- não conhece o
ProdutoDao
. Precisamos indicar para o VRaptor que ele tem que registrar oProdutoDao
no Spring, com a anotação@Component
. -
Anote a classe
ProdutoDao
com@Component
, para indicar que essa classe é uma dependência e pode ser instanciada pelo VRaptor sempre que necessário.package br.com.caelum.goodbuy.dao; // import's import br.com.caelum.vraptor.ioc.Component; @Component public class ProdutoDao { //... }
-
Se acessarmos de novo a URI que executa o método lista:
http://localhost:8080/goodbuy/produtos/lista
veremos a listagem de produtos: -
Abra o arquivo
header.jspf
e procure a div comid="menu"
. Adicione um link ao menu, para acessar a listagem de produtos:<div id="menu"> <ul> <li><a href="<c:url value="/produtos/lista"/>">Lista Produtos</a></li> </ul> </div>
8.5 - Cadastrando um produto
Para cadastrar um produto, bastar passarmos esse produto para o método salva
da classe ProdutoDao
.
Vamos criar então um método no ProdutoController
para adicionar produtos:
1 package br.com.caelum.goodbuy.controller; 2 3 // import's 4 5 @Resource 6 public class ProdutosController { 7 8 public void adiciona(Produto produto) { 9 ProdutoDao dao = new ProdutoDao(); 10 dao.salva(produto); 11 } 12 13 }
8.6 - Criando o formulário HTML
Nossa lógica para criar (salvar) produtos já está pronta. Mas como passar um produto
populado para o método adiciona
do ProdutosController
? Uma saída seria aproveitar
a injeção de dependências do VRaptor, receber um HttpServletRequest no construtor, e
pegar os parâmetros que vieram do formulário, e usar os getters e setters do Produto
para populá-lo. Mas isso é bem chato e repetitivo: imagine se o Produto tivesse 20
campos!
Para evitar isso, o VRaptor possui uma convenção para popular os parâmetros da sua lógica.
O método adiciona
recebe como parâmetro um Produto
chamado produto
, então para
popular o campo nome
desse produto, o VRaptor vai usar um parâmetro da requisição chamado
produto.nome
. Para o campo descricao
o parâmetro vai ser produto.descricao
.
Ou seja, a partir do nome do parâmetro do método, podemos usar pontos para setar os campos
do parâmetro, desde que você tenha getters e setters para eles.
O que vale é o nome do parâmetro: se o método adiciona fosse
public void adiciona(Produto novoProduto) {...}
o VRaptor iria usar os parâmetros novoProduto.nome
, novoProduto.descricao
, etc para
popular os campos do produto.
Reflection no nome dos parâmetros
Infelizmente, o Java não realiza reflection em cima de parâmetros, esses dados não ficam
disponíveis em bytecode (a não ser se compilado em debug mode, porém é algo opcional). Isso
faz com que a maioria dos frameworks que precisam desse tipo de informção criem uma anotação
própria para isso, o que polui muito o código (exemplo no JAX-WS, onde é comum encontrar um
método como o acima com a assinatura void add(@WebParam(name="cliente") Cliente cliente)
.
O VRaptor tira proveito do framework Paranamer (http://paranamer.codehaus.org), que consegue tirar essa informação através de pré compilação ou dos dados de debug, evitando criar mais uma anotação. Alguns dos desenvolvedores do VRaptor também participam no desenvolvimento do Paranamer.
Agora que sabemos como o VRaptor popula os parâmetros do método, vamos criar um formulário para
poder cadastrar produtos. Esse formulário deverá ficar na pasta WEB-INF/jsp/produtos
,
com o nome de formulario.jsp
.
1 <form action="adiciona"> 2 <fieldset> 3 <legend>Adicionar Produto</legend> 4 5 <label for="nome">Nome:</label> 6 <input id="nome" type="text" name="produto.nome"/> 7 8 <label for="descricao">Descrição:</label> 9 <textarea id="descricao" name="produto.descricao"></textarea> 10 11 <label for="preco">Preço:</label> 12 <input id="preco" type="text" name="produto.preco"/> 13 14 <button type="submit">Enviar</button> 15 </fieldset> 16 </form>
Como nossas páginas ficam sempre na pasta WEB-INF
, não temos como acessar diretamente pelo browser um jsp.
Para acessar a página que acabamos de criar, vamos criar um método na classe ProdutosController
para
chamar nossa página, usando a convenção do VRaptor.
@Resource public class ProdutosController { // atributo, construtor e métodos public void formulario() { } }
Pelo browser, chamaremos a seguinte URL:
http://localhost:8080/goodbuy/produtos/formulario
Após preencher as informações do formulário e enviar, o VRaptor vai invocar o método
adiciona
, passando o Produto populado com os campos que digitamos. E como diz a convenção
do VRaptor, precisamos criar o arquivo adiciona.jsp
, na pasta WEB-INF/jsp/produtos
.
Por enquanto vamos colocar o seguinte conteúdo:
Produto adicionado com sucesso!
8.7 - Exercícios
-
Crie o método
adiciona
que recebe umProduto
como parâmetro e faça a chamada ao métodosalva
dodao
passando esse produto.1 package br.com.caelum.goodbuy.controller; 2 public class ProdutosController { 3 4 //... 5 6 public void adiciona(Produto produto) { 7 dao.salva(produto); 8 } 9 10 }
-
Na pasta
WEB-INF/jsp/produtos
, crie o arquivoformulario.jsp
. -
Abra o arquivo que acabamos de criar e coloque os campos para de digitação, assim como o botão e o formulário.
<form action="adiciona"> <fieldset> <legend>Adicionar Produto</legend> <label for="nome">Nome:</label> <input id="nome" type="text" name="produto.nome"/> <label for="descricao">Descrição:</label> <textarea id="descricao" name="produto.descricao"></textarea> <label for="preco">Preço:</label> <input id="preco" type="text" name="produto.preco"/> <button type="submit">Enviar</button> </fieldset> </form>
-
Na pasta
WEB-INF/jsp/produtos
, crie o arquivoadiciona.jsp
com o conteúdo.<h3>Produto adicionado com sucesso!</h3>
Acesse a listagem de produtos e veja os produtos que existem.
Acesse a URI do formulário e adicione um produto. http://localhost:8080/goodbuy/produtos/formulario
Volte à listagem, e veja se um usuário foi cadastrado com sucesso.
-
Abra o arquivo
header.jspf
e procure a div comid="menu"
. Adicione um link dentro do menu para acessar o formulário:<div id="menu"> <ul> <li><a href="<c:url value="/produtos/formulario"/>"> Novo Produto </a></li> <li><a href="<c:url value="/produtos/lista"/>"> Lista Produtos </a></li> </ul> </div>
8.8 - Redirecionar para listagem depois de adicionar
Uma coisa não aparenta estar muito certa no nosso sistema: Quando adicionamos um produto, redirecionamos para uma página com a mensagem "Produto adicionado com sucesso". Será que essa página é suficiente? Será que não é melhor mostrar informações mais relevantes?
Após adicionar produtos podemos, por exemplo, mostrar uma lista com os produtos já adicionados. Usando a convenção do VRaptor, poderíamos colocar a lista de produtos no arquivo adiciona.jsp. Mas já temos uma lógica que lista produtos, por que não usá-la? Para isso precisamos falar para o VRaptor que não queremos usar a convenção, queremos ao invés de ir para a página adiciona.jsp, redirecionar para a lógica de listagem.
Para sobrescrever a convenção da view padrão, existe um componente do VRaptor chamado Result
.
Com ele é possível redirecionar para páginas ou para lógicas, ao final do seu método.
Primeiro precisamos de uma instância de Result
. Vamos dar um new nele? Não, e isso nem é possível
porque Result
é uma interface. Como fazer então? Como visto antes, o VRaptor usa injeção de
dependências, e como o Result é um componente conhecido pelo VRaptor, basta recebê-lo no construtor da
classe.
import br.com.caelum.vraptor.Result; @Resource public class ProdutosController { private final ProdutoDao dao; private final Result result; public ProdutosController(ProdutoDao dao, Result result) { this.dao = dao; this.result = result; } //... }
Agora que temos essa instância de Result
podemos usá-la para mudar o resultado da lógica
adiciona
do ProdutosController
. O jeito de fazer isso é dizer: "Como resultado, quero
redirecionar para a lógica ProdutosController.lista()". Ou em java:
result.redirectTo(ProdutosController.class).lista();
Dentro do método adiciona isso ficaria:
@Resource public class ProdutosController { private final ProdutoDao dao; private final Result result; public ProdutosController(ProdutoDao dao, Result result) { this.dao = dao; this.result = result; } public void adiciona(Produto produto) { dao.salva(produto); result.redirectTo(ProdutosController.class).lista(); } public void formulario() { } public List<Produto> lista() { return dao.listaTudo(); } }
Repare que não foi necessário criar arquivos de configuração para fazer essa mudança
de convenção, tudo é feito no próprio método. Ao olhar para o método adiciona fica claro
que o resultado dele não vai ser o adiciona.jsp, vai ser a lógica lista
do ProdutosController
.
Como o redirecionamento é para uma lógica do mesmo controller você pode ainda usar um comando mais enxuto:
result.redirectTo(this).lista();
Existem duas maneiras de redirecionar para uma lógica:
- redirecionamento do lado do cliente, usando o método
redirectTo
: o resultado da requisição será um código para o browser fazer outra requisição, para a URL indicada. É uma boa prática usar o redirecionamento do lado do cliente sempre após requisições POST, ou seja, sempre que submetemos formulários prevenindo, assim, que o usuário recarregue a página e resubmeta o formulário. - redirecionamento do lado do servidor, usando o método
forwardTo
: o servidor vai redirecionar internamente para a lógica especificada, desse modo é possível mudar o resultado de uma lógica sem mudanças do lado do cliente. Não use esse redirecionamento em requisições POST.
Além disso, podemos redirecionar nossa requisição para páginas usando outros métodos:
- result.forwardTo("/uma/outra/pagina.jsp"): redirecionamento do lado do servidor para a página especificada.
- result.redirectTo("/uma/outra/pagina.jsp"): redirecionamento do lado do cliente para a página especificada.
- result.of(ProdutosController.class).lista(): redireciona para a página da lógica especificada,
sem executar a lógica. Nesse caso redirecionaria para
/WEB-INF/jsp/produtos/lista.jsp
.
Existem mais redirecionamentos possíveis implementados no VRaptor. Todos eles estão disponíveis como métodos estáticos da classe Results, que você pode chamar dentro do método use do Result, por exemplo:
-
result.use(Results.logic())
. Redireciona usando uma lógica. A chamada que fizemos anteriormenteresult.redirectTo(ProdutosController.class).lista()
na verdade é um atalho pararesult.use(Results.logic()).redirectTo(ProdutosController.class).lista()
. -
result.use(Results.page())
. Redireciona usando uma página. A chamada que vimos anteriormenteresult.forwardTo("/uma/outra/pagina.jsp")
na verdade é um atalho pararesult.use(Results.page()).forwardTo("/uma/outra/pagina.jsp")
. -
result.use(Results.nothing())
: Não usa nenhuma página como resultado. -
result.use(Results.http())
: Usa como resultado status codes e Headers HTTP. Ex:result.use(Results.http()).setStatusCode(404)
-
result.use(Results.referer())
: Usa como resultado a página que originou a requisição. Usa um Header HTTP chamado Referer que não é obrigatório, e que é removido por alguns Proxies e Firewalls, então cuidado ao usar esse resultado.
8.9 - Exercícios
-
Receba um
Result
no construtor doProdutosController
e guarde-o em um atributo.import br.com.caelum.vraptor.Result; @Resource public class ProdutosController { private final ProdutoDao dao; private final Result result; public ProdutosController(ProdutoDao dao, Result result) { this.dao = dao; this.result = result; } //... }
-
Mude o resultado da lógica
adiciona
para redirecionar para a lógicalista
.@Resource public class ProdutosController { private final ProdutoDao dao; private final Result result; public ProdutosController(ProdutoDao dao, Result result) { this.dao = dao; this.result = result; } public void adiciona(Produto produto) { dao.salva(produto); result.redirectTo(this).lista(); } public void formulario() { } public List<Produto> lista() { return dao.listaTudo(); } }
-
Reinicie o tomcat para que ele carregue as suas mudanças, abra o formulário e adicione mais um produto. Repita essa operação e adicione mais produtos.
8.10 - Atualizando e removendo produtos
Para melhorarmos um pouco mais o nosso sistema, vamos incluir as operações de atualização e remoção de produtos.
Começando pela atualização, primeiro precisamos de uma lógica que mostre um formulário
com os dados do produto preenchidos. Vamos dar a essa lógica o nome de edita
.
public class ProdutosController { public void edita() {...} }
Precisamos passar para essa lógica o id do produto que queremos editar, para que possamos carregar suas informações do banco de dados. E para passar o Produto carregado para a jsp, basta retorná-lo:
public class ProdutosController { public Produto edita(Long id) { return dao.carrega(id); } }
Agora podemos criar o formulário de edição, passando os valores do produto carregado para os inputs:
<form action="altera"> <fieldset> <legend>Editar Produto</legend> <input type="hidden" name="produto.id" value="${produto.id }" /> <label for="nome">Nome:</label> <input id="nome" type="text" name="produto.nome" value="${produto.nome }"/> <label for="descricao">Descrição:</label> <textarea id="descricao" name="produto.descricao"> ${produto.descricao } </textarea> <label for="preco">Preço:</label> <input id="preco" type="text" name="produto.preco" value="${produto.preco }"/> <button type="submit">Enviar</button> </fieldset> </form>
Repare que precisamos também colocar um input hidden com o valor do produto.id
para que
saibamos qual produto será alterado. A action do form é para a lógica altera, então precisamos
criá-la no nosso controller. Como usamos os nomes dos campos do formulário do jeito certo,
podemos simplesmente receber um Produto como argumento na lógica altera.
public class ProdutosController { //... public void altera(Produto produto) { dao.atualiza(produto); } }
Assim como na lógica adiciona, fizemos um POST num formulário, então ao invés de colocar uma jsp com uma mensagem de "Produto alterado com sucesso", vamos redirecionar para a listagem de produtos.
public class ProdutosController { //... public void altera(Produto produto) { dao.atualiza(produto); result.redirectTo(this).lista(); }
Agora precisamos de um jeito fácil de editar um produto. Vamos então adicionar um link para editar o produto, na listagem:
<c:forEach items="${produtoList}" var="produto"> <tr> <td>${produto.nome }</td> <td>${produto.descricao }</td> <td>${produto.preco }</td> <td><a href="edita?id=${produto.id }">Editar</a></td> </tr> </c:forEach>
Vamos também remover produtos no nosso controller. Para remover um produto precisamos apenas do seu id, então basta criar a lógica:
public void remove(Long id) { Produto produto = dao.carrega(id); dao.remove(produto); }
Também não faz muito sentido criar uma página de resultado para a remoção, então vamos redirecionar para a listagem.
public void remove(Long id) { Produto produto = dao.carrega(id); dao.remove(produto); result.redirectTo(ProdutosController.class).lista(); }
Assim como criamos o link para a edição, vamos criar um link para a remoção:
<c:forEach items="${produtoList}" var="produto"> <tr> <td>${produto.nome }</td> <td>${produto.descricao }</td> <td>${produto.preco }</td> <td><a href="edita?id=${produto.id }">Editar</a></td> <td><a href="remove?id=${produto.id }">Remover</a></td> </tr> </c:forEach>
8.11 - Exercícios
-
Crie as lógicas para edição de produtos no
ProdutosController
:public Produto edita(Long id) { return dao.carrega(id); } public void altera(Produto produto) { dao.atualiza(produto); result.redirectTo(this).lista(); }
-
Implemente os métodos no
ProdutoDao
. Lembre-se de usar oCtrl+1
.public Produto carrega(Long id) { return (Produto) this.session.load(Produto.class, id); } public void atualiza(Produto produto) { Transaction tx = session.beginTransaction(); this.session.update(produto); tx.commit(); }
-
Crie o formulário de edição no arquivo
/WEB-INF/jsp/produtos/edita.jsp
.<form action="altera"> <fieldset> <legend>Editar Produto</legend> <input type="hidden" name="produto.id" value="${produto.id }" /> <label for="nome">Nome:</label> <input id="nome" type="text" name="produto.nome" value="${produto.nome }"/> <label for="descricao">Descrição:</label> <textarea id="descricao" name="produto.descricao"> ${produto.descricao } </textarea> <label for="preco">Preço:</label> <input id="preco" type="text" name="produto.preco" value="${produto.preco }"/> <button type="submit">Enviar</button> </fieldset> </form>
-
Modifique a listagem de produtos para incluir um link para a edição, no arquivo
/WEB-INF/jsp/produtos/lista.jsp
:<c:forEach items="${produtoList}" var="produto"> <tr> <td>${produto.nome }</td> <td>${produto.descricao }</td> <td>${produto.preco }</td> <td><a href="edita?id=${produto.id }">Editar</a></td> </tr> </c:forEach>
-
Reinicie o tomcat e acesse a listagem http://localhost:8080/goodbuy/produtos/lista. Escolha um dos produtos e clique em Editar.
Edite o produto e clique em Enviar.
-
Crie agora a lógica de remoção na classe
ProdutosController
:public void remove(Long id) { Produto produto = dao.carrega(id); dao.remove(produto); result.redirectTo(this).lista(); }
-
Implemente o método remove no
ProdutoDao
.public void remove(Produto produto) { Transaction tx = session.beginTransaction(); this.session.delete(produto); tx.commit(); }
-
Modifique a listagem de produtos, adicionando o link para remoção
<c:forEach items="${produtoList}" var="produto"> <tr> <td>${produto.nome }</td> <td>${produto.descricao }</td> <td>${produto.preco }</td> <td><a href="edita?id=${produto.id}">Editar</a></td> <td><a href="remove?id=${produto.id}">Remover</a></td> </tr> </c:forEach>
-
Reinicie o tomcat, acesse a listagem e remova algum produto.
8.12 - Discussão em sala - VRaptor
- Mas o mercado usa mais o Struts. Vale a pena trocá-lo?
- Por que tantas convenções?