Solucionado (ver solução)
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