Capítulo 8

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.

Você não está nessa página a toa

Você chegou aqui porque a Caelum é referência nacional em cursos de Java, Ruby, Agile, Mobile, Web e .NET.
Faça curso com quem escreveu essa apostila.

Consulte as vantagens do curso Web ágil com VRaptor, Hibernate e AJAX.

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

  1. 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();
      }
    }
    
  2. Crie a classe ProdutosController no pacote br.com.caelum.goodbuy.controller. Crie também o método lista, que retorna uma List<Produto>.

  3. 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 }
    
  4. 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 }
    
  5. No método lista, faça a chamada ao método listaTudo do dao.

     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 }
    
  6. Na pasta WEB-INF/jsp, crie a pasta produtos.

  7. 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>
    
  8. Acesse a URI que executa o método lista: http://localhost:8080/goodbuy/produtos/lista.

    erro-500.png
  9. 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 o ProdutoDao no Spring, com a anotação @Component.

  10. 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 {
      //...    
    }
    
  11. Se acessarmos de novo a URI que executa o método lista: http://localhost:8080/goodbuy/produtos/lista veremos a listagem de produtos:

    listagem-de-produtos.png
  12. Abra o arquivo header.jspf e procure a div com id="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 }

Seus livros de tecnologia parecem do século passado?

Conheça a Casa do Código, uma nova editora, com autores de destaque no mercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntos atuais.
Com a curadoria da Caelum e excelentes autores, é uma abordagem diferente para livros de tecnologia no Brasil. Conheça os títulos e a nova proposta, você vai gostar.

Casa do Código, livros para o programador.

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

  1. Crie o método adiciona que recebe um Produto como parâmetro e faça a chamada ao método salva do dao 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 }
    
  2. Na pasta WEB-INF/jsp/produtos, crie o arquivo formulario.jsp.

    formulario-cadastro1.png
  3. 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>
    
  4. Na pasta WEB-INF/jsp/produtos, crie o arquivo adiciona.jsp com o conteúdo.

    <h3>Produto adicionado com sucesso!</h3>
    
    formulario-cadastro2.png
  5. Acesse a listagem de produtos e veja os produtos que existem.

  6. Acesse a URI do formulário e adicione um produto. http://localhost:8080/goodbuy/produtos/formulario

  7. Volte à listagem, e veja se um usuário foi cadastrado com sucesso.

  8. Abra o arquivo header.jspf e procure a div com id="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>
    
    menu.png

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:

Além disso, podemos redirecionar nossa requisição para páginas usando outros métodos:

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:

Agora é a melhor hora de aprender algo novo

Se você gosta de estudar essa apostila aberta da Caelum, certamente vai gostar dos cursos online que lançamos na plataforma Alura. Você estuda a qualquer momento com a qualidade Caelum.

Conheça a Alura.

8.9 - Exercícios

  1. Receba um Result no construtor do ProdutosController 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;
      }
      //...
    }
    
  2. Mude o resultado da lógica adiciona para redirecionar para a lógica lista.

    @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();
      }
    
    }
    
  3. 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.

    listando-os-produtos2.pnglistando-os-produtos3.png

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

  1. 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();
    }
    
  2. Implemente os métodos no ProdutoDao. Lembre-se de usar o Ctrl+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();
    }
    
  3. 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>
    
  4. 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>
    
  5. Reinicie o tomcat e acesse a listagem http://localhost:8080/goodbuy/produtos/lista. Escolha um dos produtos e clique em Editar.

    atualizando-produtos1.png

    Edite o produto e clique em Enviar.

    atualizando-produtos2.pngatualizando-produtos3.png
  6. 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();
    }
    
  7. Implemente o método remove no ProdutoDao.

    public void remove(Produto produto) {
      Transaction tx = session.beginTransaction();
      this.session.delete(produto);
      tx.commit();
    }
    
  8. 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>
    
  9. Reinicie o tomcat, acesse a listagem e remova algum produto.

    removendo-produtos1.pngremovendo-produtos2.png

Você pode também fazer o curso FJ-28 dessa apostila na Caelum

Querendo aprender ainda mais sobre o framework VRaptor? Esclarecer dúvidas dos exercícios? Ouvir explicações detalhadas com um instrutor?
A Caelum oferece o curso FJ-28 presencial nas cidades de São Paulo, Rio de Janeiro e Brasília, além de turmas incompany.

Consulte as vantagens do curso Web ágil com VRaptor, Hibernate e AJAX.

8.12 - Discussão em sala - VRaptor