Solucionado (ver solução)
Solucionado
(ver solução)
13
respostas

Validação do campo Id (ajuda)..

Pessoal, boa tarde!

Estou montando um cadastro de produtos para fixar as aulas de Spring MVC e Hibernate, porém travei na seguinte situação:

O meu campo Id é atribuído manualmente pelo "usuário". Preciso fazer uma consulta no banco de dados antes de adicionar o produto, pois é lançado uma exception caso a chave primaria já exista.

Gostaria de saber se existe uma forma simples de realizar essa validação.

Obs.: Criar no DAO uma consulta, mas não estou conseguindo incluir para validação.

Controller

@RequestMapping(method = RequestMethod.POST)
    public ModelAndView gravar(@Valid Produto produto, BindingResult result, 
            RedirectAttributes redirectAttributes) {
        if (result.hasErrors()) {
            return pform(produto);

        } 
        dao.gravar(produto);
        redirectAttributes.addFlashAttribute("mensagem", "Produto " + produto.getCodOriginal() + " cadastrado com sucesso!");
        return new ModelAndView("redirect:produto");

DAO

    public void gravar(Produto produto) {
        manager.persist(produto);
    }

    public Produto idCodOriginal(String idCodOriginal) {
        return manager.find(Produto.class, idCodOriginal);
    }
13 respostas

Fala ai Thiago, tudo bem ?

Cara, o usuário precisa mesmo passar o id ? Isso não tem como ser automático ?

Caso contrário, toda vez antes de uma inserção você vai precisar validar se aquele id já existe, ai nasce um problema bem interessante, pode ser que você queira reaproveitar a mesma página para edição e nesse caso se já existir você terá apenas que mandar um merge para o banco, só que pensa que o usuário quer adicionar um novo, errou o id e colocou um que já existia, você vai fazer a substituição de um produto.

Olá Matheus, tudo em paz e com vc?

Sim, o usuário passa o id.Meu pensamento: Caso a geração seja automática (@GeneratedValue), o usuário vai poder incluir o mesmo produto duas vezes, pois o id é autoincrement.

Ai você pode fazer uma validação para ver se já não existe esse produto, não é muito bacana você deixar o usuário colocar o id, você pode ter problemas com isso.

Entendi, mas como é apenas um treino que estou fazendo, gostaria de tentar dessa forma.

Como eu faria essa validação?

Não sei se terei que mudar a Jsp, Controller ou DAO.

Por favor, pode me ajudar?

Você precisa no seu controller fazer a validação do produto.

Fazendo uma busca no banco para ver se ele já existe, é o melhor jeito dessa maneira.

Matheus,

Criei um método novo e alterei o que adiciona. Funcionou normalmente a validação quando já existe o id, mas caso entra no "if" para adicionar no banco, apresenta uma NullPointerException!

Controller


    public Produto codOriginal(String codOriginal) {
        return dao.codOriginal(codOriginal);

    }

    @RequestMapping(method = RequestMethod.POST)
    public ModelAndView gravar(@Valid Produto produto, BindingResult result, RedirectAttributes redirectAttributes) {    

if (result.hasErrors()) {

            return pform(produto);
        } else {

            Produto testeId = codOriginal(produto.getCodOriginal());

            if (testeId.equals(null) || testeId.equals("")) {
                dao.gravar(produto);
                redirectAttributes.addFlashAttribute("mensagem",
                        "Produto " + produto.getCodOriginal() + " cadastrado com sucesso!");

                return new ModelAndView("redirect:produto");

            } else {
                redirectAttributes.addFlashAttribute("mensagem",
                        "O produto " + produto.getCodOriginal() + " já possui cadastro!");

                return new ModelAndView("redirect:produto");

            }
        }

    }

Thiago, qual linha tá dando erro ?

Olá, bom dia!

Apresenta o erro na linha "dao.gravar(produto);"...

Erro:

GRAVE: Servlet.service() for servlet [dispatcher] in context with path [/oficina] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause java.lang.NullPointerException at br.com.oficina.cotacao.controller.ProdutoController.gravar(ProdutoController.java:52)

Duas coisas podem estar nulas ai, o seu dao ou seu produto.

Confirma se você tem instancias válidas para ambos

As informações são inseridas no formulário da pagina, não sei porque estão se perdendo após realizar esse teste " if (testeId.equals(null) || testeId.equals("")) ".

Se o teste é falso retorna corretamente, porém caso seja verdadeiro não carrega as informações para adicionar no banco.

DAO

    public void gravar(Produto produto) {
        manager.persist(produto);
    }

    public Produto codOriginal(String codOriginal) {
        return manager.find(Produto.class, codOriginal);
    }

JSP.


<body>   
           <p>${teste} </p>

         <form:form action="${ s:mvcUrl('PC#gravar').build() }" method="post" commandName="produto" >
              <div>
                 <label>Código Original</label>
                <form:input path="codOriginal"/>
                <form:errors path="codOriginal" />         

              </div>
              <div>
                 <label>Código Fábrica</label>
                 <form:input path="codFabrica"/>
                 <form:errors path="codFabrica" />

              </div>
              <div>
                 <label>Nome do Produto</label>
                 <form:input path="nome"/>
                 <form:errors path="nome" />
              </div>

           <button type="submit">Cadastrar</button>            

         <p>${mensagem} </p>             

        </form:form>
solução!

Fala Thiago, tudo bem ?

Fica difícil ver qual a causa do NullPointer só olhando o código aqui. Você poderia verificar se tem referencia pro dao ou pro produto como o matheus comentou.

Mas independente disso você poderia tentar algo assim:

form:

<form:form action="${ s:mvcUrl('PC#gravar').build() }" method="post" commandName="produto" >
    <div>
        <label>Código Original</label>
        <form:input path="codOriginal"/>
        <form:errors path="codOriginal" />
    </div>
    ...

controller:

@Autowired
private ProdutoDao produtoDao;
...

@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.addValidators(new ProdutoValidator(produtoDao));
}

@RequestMapping(method = RequestMethod.POST)
public ModelAndView gravar(@Valid Produto produto, BindingResult result, RedirectAttributes redirectAttributes) {
    if (result.hasErrors()) {
        return pform(produto);
    }
    ...
    dao.salva(produto);
    return "redirect:/produtos";
}

validator:

public class ProdutoValidator implements Validator {

    private ProdutoDao produtDao;

    public ProdutoValidator(ProdutoDao produtoDao) {
        this.produtoDao = produtoDao;
    }

    @Override
    public void validate(Object target, Errors errors) {

        Produto produto = (Produto) target;

        if (dao.buscaPorId(produto.getCodigo()) != null) {
            errors.rejectValue("codigo", "codigo.existente", "Já existe um produto cadastrado com esse codigo");
        }
    }

    // aqui a gente só diz pro Spring se ele pode aplicar ou não esse validator para o alvo validado. No caso, somente será aplicado se o alvo for um produto
    @Override
    public boolean supports(Class clazz) {
        return Produto.class.isAssignableFrom(clazz);
    }

}

dao:

public Produto buscaPorId(Integer id) {
    // retorna a entidade encontrada ou nulo se nao existir esse id
    return entityManager.find(Produto.class, id);
}

Explicando: Você pode criar um Validator pra produto aproveitando o Spring Validator (implements Validator). O Spring então vai aplicar a validação durante o processo de binding (assim como ele aplica as validações da Bean Validation - @NotNull nas propriedades de produto, por exemplo). Independente de qual erro de validação for aplicado ele adiciona os problemas no mesmo objeto Errors verificado no binding result.

A implementação fica bem simples. Você poderia apenas chamar um findById e se vier algo diferente de nulo significa que ja tem registro salvo com aquele codigo. Então poderia adicionar um erro de validação ao processo de bind errors.rejectValue("codigo", "codigo.existente", "Já existe produto cadastrado com esse código"), onde a primeira String é o atributo com erro, a segunda é uma chave se quiser chavear mensagens de validação, e a terceira seria a própria mensagem padrão pra esse erro de validação.

Você não precisa necessariamente usar o Validator do Spring, mas em geral o fluxo e a lógica pra esse tipo de validação é a mesma.

@RequestMapping(method = RequestMethod.POST)
public ModelAndView gravar(@Valid Produto produto, BindingResult result, RedirectAttributes redirectAttributes) {

    SeuValidator seuValidator = new SeuValidator(dao);
    if (!seuValidator.valida(produto)) {
        result.rejectValue("codigo", "codigo.existente");
    }
    // ou voce poderia passar a referencia do BindingResult pro validator pra nao deixar esse codigo aqui no controller. etc

    if (result.hasErrors()) {
        return pform(produto);
    }
    ...
    dao.salva(produto);
    return "redirect:/produtos";
}

Espero ter ajudado. Abraço!

Rafael, boa tarde! Tudo em paz e com vc?

Cara, deu certo!!

Validator
    @Override
    public void validate(Object target, Errors errors) {
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "codOriginal", "field.required");

        Produto produto = (Produto) target;

            if (dao.codOriginal(produto.getCodOriginal()) != null) {

            errors.rejectValue("codOriginal", "testeId");

             }

}
Controller
@InitBinder
    public void InitBinder(WebDataBinder binder) {
        binder.addValidators(new ProdutoValidation(dao));
    }

    @RequestMapping(method = RequestMethod.POST)
    public ModelAndView gravar (@Valid Produto produto, BindingResult result, RedirectAttributes redirectAttributes) {

        if (result.hasErrors()) {

            return pform(produto);
        }

        dao.gravar(produto);
        redirectAttributes.addFlashAttribute("mensagem",
                "Produto " + produto.getCodOriginal() + " cadastrado com sucesso!");

        return new ModelAndView("redirect:produto");
    }

Muito obrigado pela sua ajuda e do Matheus.

Forte Abraço!!

Perfeito Thiago! mandou bem

Em geral é a melhor prática mesmo deixar as mensagens nos .properties e usar a chave pra buscar.

Abraço e bons estudos!