Cenário de Negócio
O fluxo operacional para edição de um produto segue estas etapas:
- O usuário acessa a listagem de produtos disponíveis
- Ao clicar em um item, a página de edição é carregada com os dados recuperados do banco
- Após alterar as informações, o usuário confirma o envio do formulário
Mapeamento de Consulta no MyBatis
O arquivo de mapeamento define as consultas SQL utilizadas pela camada de persistência:
<mapper namespace="com.app.mapper.ProductMapper">
<sql id="ColunasPrincipais">
id, nome, valor, imagem, data_cadastro
</sql>
<sql id="ColunaTextoLongo">
descricao
</sql>
<resultMap id="MapaResultadoBase" type="com.app.model.Produto">
<id column="id" property="identificador" jdbcType="INTEGER"/>
<result column="nome" property="nome" jdbcType="VARCHAR"/>
<result column="valor" property="valor" jdbcType="REAL"/>
<result column="imagem" property="imagem" jdbcType="VARCHAR"/>
<result column="data_cadastro" property="dataCadastro" jdbcType="TIMESTAMP"/>
</resultMap>
<resultMap id="MapaResultadoCompleto" type="com.app.model.Produto" extends="MapaResultadoBase">
<result column="descricao" property="descricao" jdbcType="LONGVARCHAR"/>
</resultMap>
<select id="buscarPorId" resultMap="MapaResultadoCompleto" parameterType="java.lang.Integer">
SELECT <include refid="ColunasPrincipais"/>, <include refid="ColunaTextoLongo"/>
FROM produtos
WHERE id = #{codigo,jdbcType=INTEGER}
</select>
<update id="atualizarProdutoCompleto" parameterType="com.app.model.ProdutoDetalhado">
UPDATE produtos
SET nome = #{nome,jdbcType=VARCHAR},
valor = #{valor,jdbcType=REAL},
imagem = #{imagem,jdbcType=VARCHAR},
data_cadastro = #{dataCadastro,jdbcType=TIMESTAMP},
descricao = #{descricao,jdbcType=LONGVARCHAR}
WHERE id = #{identificador,jdbcType=INTEGER}
</update>
</mapper>
Interface e Implementação do Serviço
public interface ProdutoService {
List<ProdutoDetalhado> listarProdutos(FiltroProduto filtro) throws Exception;
ProdutoDetalhado buscarProdutoPorId(Integer codigo) throws Exception;
void salvarAlteracoes(Integer codigo, ProdutoDetalhado produtoAtualizado) throws Exception;
}
package com.app.service.impl;
import java.util.List;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import com.app.mapper.ProdutoMapper;
import com.app.mapper.ProdutoMapperCustom;
import com.app.model.Produto;
import com.app.model.ProdutoDetalhado;
import com.app.model.FiltroProduto;
import com.app.service.ProdutoService;
public class ProdutoServiceImpl implements ProdutoService {
@Autowired
private ProdutoMapperCustom mapperCustom;
@Autowired
private ProdutoMapper mapperPadrao;
@Override
public List<ProdutoDetalhado> listarProdutos(FiltroProduto filtro) throws Exception {
return mapperCustom.consultarProdutos(filtro);
}
@Override
public ProdutoDetalhado buscarProdutoPorId(Integer codigo) throws Exception {
Produto registro = mapperPadrao.buscarPorId(codigo);
ProdutoDetalhado resultado = new ProdutoDetalhado();
BeanUtils.copyProperties(registro, resultado);
return resultado;
}
@Override
public void salvarAlteracoes(Integer codigo, ProdutoDetalhado produtoAtualizado) throws Exception {
if (codigo == null) {
throw new IllegalArgumentException("O identificador do produto é obrigatório");
}
produtoAtualizado.setIdentificador(codigo);
mapperPadrao.atualizarProdutoCompleto(produtoAtualizado);
}
}
Camada do Controlador
@Controller
@RequestMapping("/produtos")
public class ProdutoController {
@Autowired
private ProdutoService servicoProduto;
@RequestMapping("/listar")
public ModelAndView listarProdutos() throws Exception {
List<ProdutoDetalhado> lista = servicoProduto.listarProdutos(null);
ModelAndView mv = new ModelAndView();
mv.addObject("listaProdutos", lista);
mv.setViewName("produtos/listagem");
return mv;
}
@RequestMapping(value = "/editar", method = {RequestMethod.GET, RequestMethod.POST})
public String exibirEdicao(Model modelo) throws Exception {
ProdutoDetalhado produto = servicoProduto.buscarProdutoPorId(1);
modelo.addAttribute("produtoSelecionado", produto);
return "produtos/formularioEdicao";
}
@RequestMapping("/confirmarEdicao")
public String processarEdicao(HttpServletRequest requisicao) throws Exception {
return "redirect:listar.action";
}
}
Aspectos Importantes do @RequestMapping
Mapeamento de URLs
Esta anotação associa métodos do controlador a caminhos específicos da aplicação, permitindo que o Spring MVC direcione requisições HTTP ao handler correto.
Restrição por Namespace
Ao colocar @RequestMapping("/produtos") na classe, todos os métodos internos herdam esse prefixo. Por exemplo, o método mapeado como "/editar" responde efetivamente à URL /produtos/editar.
Filtragem por Método HTTP
É possível restringir quais verbos HTTP acionam determinado endpoint:
@RequestMapping(value = "/editar", method = {RequestMethod.POST, RequestMethod.GET})
Tipos de Retorno dos Métodos do Controladro
ModelAndView
Encapsula tanto os dados (modelo) quanto a identificação da view. É a abordagem clássica quando se deseja controlar ambos os aspectos em um único objeto de retorno.
String (Nome Lógico da View)
O valor retornado representa o nome lógico da página. O Spring resolve o caminho real combinando prefixo + nome lógico + sufixo configurados no ViewResolver.
Quando se usa Model como parâmetro do método, os atributos adicionados ficam disponíveis na view:
modelo.addAttribute("produtoSelecionado", produto);
Redirecionamento (redirect)
O prefixo redirect: instrui o navegador a fazer uma nova requisição para o endereço indicaod. A URL visível na barra de endereços muda e o escopo original da requisição é perdido — os dados não são transferidos automaticamente. Caso seja necessário enviar parâmetros, eles devem ser incluídos diretamente na URL de destino:
return "redirect:listar.action?status=sucesso";
Internamente, o comportamento equivale a chamar response.sendRedirect(). Como uma requisição completamente nova é disparada, o request original não é compartilhado.
Encaminhamento (forward)
O prefixo forward: realiza o encaminhamento no lado do servidor. A URL exibida no navegador permanece inalterada e o objeto request original é preservado, permitindo acesso a todos os atributos já definidos.
return "forward:listar.action";
Esse mecanismo corresponde ao uso de RequestDispatcher.forward(), onde nenhuma nova requisição é criada.
Retorno void
Quando o método não retorna valor, o controlador manipula a resposta diretamente pelos objetos HttpServletRequest e HttpServletResponse injetados como parâmetros.
Encaminhamento via request:
requisicao.getRequestDispatcher("/WEB-INF/jsp/produtos/listagem.jsp")
.forward(requisicao, resposta);
Redirecionamento via response:
resposta.sendRedirect("/produtos/listar.action");
Escrita direta de conteúdo JSON:
resposta.setCharacterEncoding("UTF-8");
resposta.setContentType("application/json;charset=UTF-8");
resposta.getWriter().write("{\"mensagem\":\"Operacao realizada com sucesso\"}");