Solucionado (ver solução)

Importante

Você está vendo a versão anterior da nova experiência da Alura que estamos preparando para você. Em breve, ela ganha uma identidade visual novinha totalmente pensada em potencializar seus estudos!

Solucionado
(ver solução)
10
respostas

Dúvida sobre a tag <spring:select>

Boa noite senhores,

Meu nome é Rafael e estou usando uma tag do spring que é para carregar uma select de departamentos. Basicamente seria um formulário de empregados e nele, uma select de departamentos é carregada. Só que, infelizmente, não estou conseguindo levar o id do departamento para a tabela empregado. Gostaria, que alguém me orientasse em como resolver esse problema.

segue abaixo a linha de código:

Arquivo add.js

<div class="form-group">
    <label for="for_depatamento">Departamento</label>
    <form:select path="departamento" cssClass="form-control">
    <form:options items="${departamentos}" itemValue="id" itemLabel="nome"></form:options>
    </form:select>        
</div>

Controller de Empregado

@Controller
@RequestMapping(value="/empregado")
public class EmpregadoController {

    @Autowired
    private EmpregadoDAO empDAO;

    @Autowired
    private DepartamentoDAO depDAO;

    @RequestMapping(value="/add", method=RequestMethod.GET)
    public ModelAndView addEmpregado(@ModelAttribute("empregado") Empregado empregado, 
                                    ModelMap model) {
        model.addAttribute("departamentos", depDAO.getListAllDepartamentoDAO());
        return new ModelAndView("empregado/add", model);
    }

    @RequestMapping(value="/save", method = RequestMethod.POST)
    public ModelAndView saveEmpregado(@ModelAttribute("empregado") Empregado umEmpregado,
                                      BindingResult result,    
                                      RedirectAttributes attr) {
        ModelAndView model;
        System.out.println("Empregado" + umEmpregado);
        if(result.hasErrors()) {
            model = new ModelAndView("/empregado/add"); 
        }

        empDAO.saveEmpregadoDAO(umEmpregado);
        attr.addFlashAttribute("message","Registro inserido com sucesso");
        model = new ModelAndView("redirect:/empregado/add");

        return model;
    }
...

A classe de persistencia EmpregadoDAOImpl

@Repository
@Transactional
public class EmpregadoDAOImpl implements EmpregadoDAO{

    @PersistenceContext
    EntityManager manager = null;

    @Override
    public void saveEmpregadoDAO(Empregado umEmpregado) {
        manager.persist(umEmpregado);
    }
...

A classe Empregado

package br.com.metatron.models;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name = "Empregado")
public class Empregado {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "matricula", nullable = false, length = 20)
    private String matricula;

    @Column(name = "CPF", nullable = false, length = 20)
    private String CPF;

    @Column(name = "nome" , nullable = false, length = 100)
    private String nome;

    @Column(name = "endereco", nullable = false, length = 100)
    private String endereco;

    @ManyToOne
    private Departamento departamento;

Os métodos get e sets foram omitidos. Quando executo o sistema ele me mostra a seguinte messagem: NullPointerException.

EmpregadoEmpregado [id=null, matricula=201608155705, CPF=123456789, nome=Maria Kiteria, endereco=Rua Furtado de Mendonça, departamento=null]
Hibernate: 
    insert 
    into
        Empregado
        (CPF, departamento_id, endereco, matricula, nome) 
    values
        (?, ?, ?, ?, ?)
Hibernate: 
    select
        departamen0_.id as id1_0_,
        departamen0_.nome as nome2_0_,
        departamen0_.sigla as sigla3_0_ 
    from
        departamento departamen0_

Como pode ser visto acima, o departamento é passado como null. Sendo inserido o banco de dados assim mesmo.

Muito obrigado pela sua atenção Rafael

10 respostas

No form, deve ser departamento.id.

Bom Dia !

Rafael,

Conseguiu resolver o problema?

Bom dia Sayd!

Infelizmente não consegui, já tentei de tudo e nada. Você teria alguma ideia ?

Obrigado, Rafael

Rafael, tenho uma solução, mais não sei se é a mais adequada.

Durante o curso eu criei outras Entidades que se relacionava, aí tive o mesmo problema que você. Eu informava o id da Entidade no formulário mais o Spring não conseguia fazer a Conversão.

A solução é composta por alguns passos:

1º Passo, é criar uma classe que implementa a Interface Converter, essa interface obriga você a escrever o método convert(String param) . O parâmetro será o id lá do formulário. Essa informação já vai automática para o método. Essa classe pode ser escrita da seguinte forma.

import org.springframework.core.convert.converter.Converter;
public class DepartamentoConverter implements Converter<String, Departamento.class> {

    private DepartamentoDAO departamentoDAO;

    public DepartamentoConverter() {
    }

    public DepartamentoConverter(DepartamentoDAO departamentoDAO) {
        this.departamentoDAO= departamentoDAO;

    }

    @Override
    public Departamento convert(String param) {

        Departamento departamento = departamentoDAO.buscarPorId(Integer.parseInt(param));
        departamento;
    }

}

2º Passo, fazer a adição de algumas informações na sua classe de configuração do SpringMVC, a classe que extend a WebMvcConfigurerAdapter .Possivelmente nessa classe você tenha um método chamado mvcConversisonService(), nesse método você irá adicionar alguns comandos para informar o Spring que temos uma classe que sabe converter uma String (id) para Departamento:

@Autowired
private DepartamentoDAO departamentoDAO;
@Bean
    public FormattingConversionService mvcConversionService() {
        DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(true);
        conversionService.addConverter(new DepartamentoConverter(departamentoDAO));
        return conversionService;
    }

Observações: Não sei porque raios, eu não consegui injetar o DAO diretamente na classe que implementa o Converter, a solução foi injetar ela nesse ponto.

Acredito que com isso, você consiga ter o resultado esperado.Qualquer dúvida estou a disposição !Boa Sorte !

Gente, calma aí. Testou trocando o valor da path para departamento.id e não funcionou? Qual erro que deu? É para ser simples mesmo.

Boa Tarde Alberto,

Agradecendo a ajuda do Sayd, prefiro começar pelo modo simples mesmo. ok!

segue o código abaixo:

<div class="form-group">
                <label for="for_depatamento">Departamento</label>
                <form:select path="departamento.id" cssClass="form-control">
                    <form:option value="0" label="---Selecione o Departamento---" />
                    <form:options  items="${departamentos}" itemValue="id" itemLabel="nome" />
                </form:select>
              </div>

Quando uso no path="departamento.id" ele me gera este erro:

org.springframework.beans.NotWritablePropertyException: Invalid property 'departamento' of bean class [br.com.metatron.models.Empregado]: Bean property 'departamento' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?

Quando uso desta forma o path=${departamento.id}, ele executa, mas não pega o id do Departamento. Passa como null.

Segue abaixo a classe Empregado

package br.com.metatron.models;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name = "Empregado")
public class Empregado {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "matricula", nullable = false, length = 20)
    private String matricula;

    @Column(name = "CPF", nullable = false, length = 20)
    private String CPF;

    @Column(name = "nome" , nullable = false, length = 100)
    private String nome;

    @Column(name = "endereco", nullable = false, length = 100)
    private String endereco;

    @ManyToOne
    private Departamento departamento;

    @Deprecated
    public Empregado() {

    }


    public Empregado(Long id, String matricula, String cPF, String nome, String endereco) {
        super();
        this.id = id;
        this.matricula = matricula;
        CPF = cPF;
        this.nome = nome;
        this.endereco = endereco;
    }


    public Empregado(String matricula, String CPF, String nome, String endereco) {
        super();
        this.matricula = matricula;
        this.CPF = CPF;
        this.nome = nome;
        this.endereco = endereco;
    }

    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getMatricula() {
        return matricula;
    }

    public void setMatricula(String matricula) {
        this.matricula = matricula;
    }

    public String getCPF() {
        return CPF;
    }

    public void setCPF(String CPF) {
        this.CPF = CPF;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public String getEndereco() {
        return endereco;
    }

    public void setEndereco(String endereco) {
        this.endereco = endereco;
    }

    public Departamento getDepartamento() {
        return departamento;
    }

    public void setDep(Departamento departamento) {
        this.departamento = departamento;
    }

    @Override
    public String toString() {
        return "Empregado [id=" + id + ", matricula=" + matricula + ", CPF=" + CPF + ", nome=" + nome + ", endereco="
                + endereco + ", departamento=" + departamento + "]";
    }

}

Alberto o que me sugure? Obrigado, Rafael

Boa Tarde !

Alberto,

No meu caso acontece o erro da imagem: https://ibb.co/gRX9kH .O BindingResult fala que não foi possível Converter o id [1] para um tipo conhecido, alguma coisa assim.O HTML ficou assim:

<select id="autores" name="autores" class="form-control" multiple="multiple">
    <option value="1">Sayd Rodrigo</option>
    <option value="2">Valdivino de Araujo Aquino</option>
</select><input type="hidden" name="_autores" value="1"/>

A classe Produto está da seguinte forma:

@Entity
public class Produto {
    @Id
    @GeneratedValue
    private int id;

    private String titulo;
    private String descricao;

    private int paginas;

    @ElementCollection(fetch = FetchType.EAGER)
    private List<Preco> precos;

    @DateTimeFormat
    private Calendar dataLancamento;

    private String sumarioPath;

    @ManyToMany(fetch = FetchType.EAGER)
    private List<Autor> autores;
....

Na ocasião também achei que o Spring faria isso de forma fácil, mais não consegui a solução, aguardo a sugestão assim como o Rafael.

Ta, casos diferentes. No de Rafael:

Você tem um método chamado setDep, ele deveria se chamar setDepartamento. O Spring não está conseguindo chamar o método automaticamente para você e, consequentemente, lança exception.

O segundo caso já é mais complicado... Você espera uma lista de ids. Minha sugestão: criar uma classe que imita o formulário, com os mesmos campos que você espera. No caso da lista de ids, basta que você crie uma List que vai receber os ids.. Depois, para cada id, você carrega o objeto necessário.

Bom Dia !

Alberto,

Na situação abaixo eu espero apenas um objeto, e mesmo assim, acontece o mesmo erro de quando eu espero uma Lista.

Consegue me ajudar?

Imagem: https://ibb.co/fbuDxx

HTML:

<div class="form-group">
            <label for="empresa">Empresa</label>
            <select id="empresa" name="empresa" class="form-control"><option value="2">Centauro Gr&aacute;fica</option></select>
        </div>
@Entity
public class Departamento {
    @Id @GeneratedValue
    private int id;

    @NotBlank
    private String nome;

    @ManyToOne
    private Empresa empresa;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public Empresa getEmpresa() {
        return empresa;
    }

    public void setEmpresa(Empresa empresa) {
        this.empresa = empresa;
    }

}
solução!

Bom dia Alberto,

Consegui resolver o problema do ID, era aquilo mesmo que você tinha falado. Não precisava ter feito tudo isso que o Sayd fez.

Depois de ter quebrado a cabeça e de ter consultado a documentação. Consegui.

Segue o código abaixo do add.jsp

<div class="form-group">
                <label for="for_departamento">Departamento</label>

                     <form:select path="departamento.id" cssClass="form-control">

                     <c:if test="${umDepartamento != null  }"> <!-- Caso seja para atualizar  -->
                         <c:set value="Atualizar" var="btnNome"></c:set>

                         <form:options items="${departamentos }" itemValue="id" itemLabel="nome" />
                     </c:if>
                     <c:if test="${umDepartamento == null  }"> <!-- Caso seja para adicionar  -->
                         <c:set value="Salvar" var="btnNome"></c:set>

                         <form:options items="${departamentos }" itemValue="id" itemLabel="nome" />
                     </c:if>                                      
                </form:select>

              </div>

Como você pode ver, no path está como você tinha falado, departamento_id.

PS: umDepartamento é o id retornado no BD, para preenchimento da select do departamento, onde o empregado trabalha.

Valeu Alberto, Obrigado! Rafael