SelectOneMenu + converter = erro de validação

Isso é um problema bem comum, e para quem está começando com JSF (ou mesmo já é veterano de guerra) acaba perdendo um tempinho considerável para soluciona-lo. Sendo, vou explicar o cenário abaixo, logo vou explanar o problema e finalizo com a solução para o mesmo.

O cenário

Imagine que você queira uma combobox (h:selectOneMenu) para exibir uma lista de municípios (objetos do tipo Municipio) para o usuário, daí quando o usuário selecionar o valor na combo e submeter o formulário este valor selecionado será inserido em um atributo do tipo Municipio no teu managed bean. Até aí tudo, nada de complicado, basta implementar um JSF custom converter, algo como MunicipioConverter, para converter o valor selecionado no cliente (na maioria das vezes é o id do item) para um objeto do tipo Municipio e pronto, resolvido o problema! A questão é que ao submeter o formulário você receberá de cara este erro:

Validation Error: Value is not valid

E você não entenderá o porquê dele está ocorrendo já que teu converter, managed bean e página estão corretamente implementados e configurados.

O problema

Bem, já está óbvio que o erro ocorre na Fase de Validação do ciclo de vida do JSF. Na fase de validação o erro ocorre porque a especificação do JSF diz que o componente UISelectOne deve assegurar que o valor selecionado esteja dentro dos valores selecionáveis exibidos ao usuário, ou seja, que o valor selecionado seja igual a um dos valores avaliados, caso isso não ocorra então será ecoado o erro de validação acima.

De acordo com o Javadoc nós temos:

In addition to the standard validation behavior inherited from UIInput, ensure that any specified value is equal to one of the available options. If it is not, enqueue an error message and set the valid property to false.

Sendo mais especifico ainda, o “erro” ocorre quando o componente (UISelectOne) testa a igualdade do teu objeto (aquele retornado pelo converter de Municipio) -que é uma nova instância- com a lista das opções avaliadas, como você não sobreescreveu os métodos equals() e hashCode() da classe Municipio a igualdade entre objetos é feito por referência, um simples "obj1 == obj2", logo por ser uma nova intância (novo endereço de memória) isso retornará false, e então o erro ecoa.

Como podem ver, isso não é erro no teu converter ou configuração da tua aplicação, mas sim uma validação imposta pela especificação. Acredito que isso ocorra para barrar valores inválidos enviados por algum cracker, enfim, é um medida de segurança básica que JSF já te fornece.

A solução

Se você está recebendo este erro provavelmente você chegou a conclusão que o teu objeto convertido e retornado pela tua implementação do custom converter (MunicipioConverter) não pertence a nenhuma das opções avaliadas pelo componente.

Sendo, a solução é simples, sobreescreva os métodos equals() e hashCode() da tua classe Municipio 😀
Pronto, assim quando entrar na fase de validação o componente chamará estes métodos e encontrará o valor selecionado entre as opções avalidadas.

Concluindo

Bem senhores, este problema ocorre quando utilizamos nossos objetos (tipos) customizados (Municipio, Pessoa, Item etc) e não sobreescrevemos os métodos para comparação entre objetos. Isso não ocorre quando utilizamos tipos primitivos, wrappers, String, BigDecimal, isto é, objetos que possuem os métodos equals() e hashCode() sobreescritos corretamente.

Enfim, isto foi um resumão de como contornar este problema, acredito que muitos já passaram por isso e quem não passou provavelmente irá passar algum dia 🙂

Isso é tudo, boa sorte.

90 Replies to “SelectOneMenu + converter = erro de validação”

  1. Excelente post!! Grande Ponte, mais uma vez dando um show… Eu não sabia desde detalhe… Muito importante saber disto….

    Obrigado, forte abraço!

  2. Ola amigo sou uma leitora assidua do seu blog, segui suas instrucoes mas ainda continuo tomando um erro de validacao,
    onde eu posso conseguir um exemplo disso para que posso comparar com meu codigo?
    abraços e parabens pelo excelente post.

  3. Olá Marcia, obrigado, é muito bom saber disso XD

    Poderia me explicar o cenário do teu problema, assim ficaria mais fácil achar o problema? Outra coisa, existe uma excelente lista de discussão sobre JSF, ela é a http://groups.google.com/group/javasf

    Caso você não esteja cadastrada nela você poderia se cadastrar, pessoal lá é bem receptivo e sempre tenta ajudar com o que pode.

    Abraços e obrigado.

  4. meu problema é o seguinte :tenho uma entidade perfil e uma usuario 1:n de perfil p/ usuario mas na hora de salvar o usuario tem que pegar o que foi selecionado no selectone que contem os perfis e salvar como objeto Perfilusuario so que nao estou conseguindo fazer isso olha o codigo:
    usuario…>
    package com.NASeguranca.Model;

    import java.util.Date;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Set;

    import javax.faces.model.SelectItem;
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.JoinColumn;
    import javax.persistence.ManyToOne;
    import javax.persistence.OneToMany;
    import javax.persistence.SequenceGenerator;
    import javax.persistence.Table;
    import javax.persistence.Temporal;
    import javax.persistence.TemporalType;

    import org.hibernate.annotations.Cascade;
    import org.hibernate.annotations.CascadeType;
    import org.hibernate.annotations.Fetch;
    import org.hibernate.annotations.FetchMode;

    import com.NASeguranca.Dao.DaoEstado;
    import com.NASeguranca.Idao.IDaoEstado;

    @Entity @Table(name=”usuario”)
    @SequenceGenerator(name = “geraCodCidade”, sequenceName = “geraCodCidade”)
    public class Usuario {
    @Id @GeneratedValue(strategy = GenerationType.AUTO, generator = “geraCodCidade”)
    private int codigousuario;
    // private int codigoPerfil;
    private String nome ;
    private String sobrenome;
    private String setor;
    private String cargo;
    @Temporal(TemporalType.DATE)
    private Date dataInclusao= new Date();
    private int senha;
    private String login;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name=”codigoperfil”,
    insertable=true, updatable=true)
    @Fetch(FetchMode.JOIN)
    @Cascade(CascadeType.SAVE_UPDATE)

    private PerfilUsuario perfilusuario;

    @OneToMany(mappedBy=”usuario”, fetch = FetchType.LAZY)
    @Cascade(CascadeType.ALL)
    private Set pedidoCompra;

    @OneToMany(mappedBy=”usuario”, fetch = FetchType.LAZY)
    @Cascade(CascadeType.ALL)
    private Set pedido;
    public Usuario(){}
    public int getCodigousuario() {
    return codigousuario;
    }
    public void setCodigousuario(int codigousuario) {
    this.codigousuario = codigousuario;
    }
    public String getNome() {
    return nome;
    }
    public void setNome(String nome) {
    this.nome = nome;
    }
    public String getSobrenome() {
    return sobrenome;
    }
    public void setSobrenome(String sobrenome) {
    this.sobrenome = sobrenome;
    }
    public String getSetor() {
    return setor;
    }
    public void setSetor(String setor) {
    this.setor = setor;
    }
    public String getCargo() {
    return cargo;
    }
    public void setCargo(String cargo) {
    this.cargo = cargo;
    }
    public Date getDataInclusao() {
    return dataInclusao;
    }
    public void setDataInclusao(Date dataInclusao) {
    this.dataInclusao = dataInclusao;
    }
    public int getSenha() {
    return senha;
    }
    public void setSenha(int senha) {
    this.senha = senha;
    }
    public String getLogin() {
    return login;
    }
    public void setLogin(String login) {
    this.login = login;
    }
    public PerfilUsuario getPerfilusuario() {
    return perfilusuario;
    }
    public void setPerfilusuario(PerfilUsuario perfilusuario) {
    this.perfilusuario = perfilusuario;
    }
    public Set getPedidoCompra() {
    return pedidoCompra;
    }
    public void setPedidoCompra(Set pedidoCompra) {
    this.pedidoCompra = pedidoCompra;
    }
    public Set getPedido() {
    return pedido;
    }
    public void setPedido(Set pedido) {
    this.pedido = pedido;
    }

    private List getLista() throws Exception {
    List lista = new LinkedList();
    Estado uf;
    IDaoEstado idao= new DaoEstado();
    List resultado = (List) idao.ListarTodos();
    for(Estado teste : resultado){

    SelectItem item = new SelectItem(teste.getCodigoUf(),teste.getSigla());
    lista.add(item);

    }
    return lista;
    }
    @Override
    public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + codigousuario;
    result = prime * result
    + ((perfilusuario == null) ? 0 : perfilusuario.hashCode());
    return result;
    }
    @Override
    public boolean equals(Object obj) {
    if (this == obj)
    return true;
    if (obj == null)
    return false;
    if (getClass() != obj.getClass())
    return false;
    final Usuario other = (Usuario) obj;
    if (codigousuario != other.codigousuario)
    return false;
    if (perfilusuario == null) {
    if (other.perfilusuario != null)
    return false;
    } else if (!perfilusuario.equals(other.perfilusuario))
    return false;
    return true;
    }

    }

    PerfilUsuario—>

    package com.NASeguranca.Model;

    import java.util.Set;

    import javax.persistence.*;

    import org.hibernate.annotations.Cascade;
    import org.hibernate.annotations.CascadeType;

    @Entity @Table(name=”perfilUsuario”)
    @SequenceGenerator(name = “geraCodPerfil”, sequenceName = “geraCodPerfil”)
    public class PerfilUsuario {
    @Id @GeneratedValue(strategy = GenerationType.AUTO, generator = “geraCodPerfil”)
    private Long codigoperfil;
    private String descricao; //adm — super—limitado

    @OneToMany(mappedBy=”perfilusuario”, fetch = FetchType.LAZY)
    @Cascade(CascadeType.ALL)
    private Set usuario;

    public PerfilUsuario(){}

    public Long getCodigoperfil() {
    return codigoperfil;
    }

    public void setCodigoperfil(Long codigoperfil) {
    this.codigoperfil = codigoperfil;
    }

    public String getDescricao() {
    return descricao;
    }

    public void setDescricao(String descricao) {
    this.descricao = descricao;
    }

    public Set getUsuario() {
    return usuario;
    }

    public void setUsuario(Set usuario) {
    this.usuario = usuario;
    }

    public String toString() {
    // Override Object#toString() so that it returns a human readable String representation.
    // It is not required by the Converter or so, it just pleases the reading in the logs.
    return “PerfilUsuario[” + codigoperfil + “,” + descricao + “]”;
    }

    @Override
    public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result
    + ((codigoperfil == null) ? 0 : codigoperfil.hashCode());
    return result;
    }

    @Override
    public boolean equals(Object obj) {
    if (this == obj)
    return true;
    if (obj == null)
    return false;
    if (getClass() != obj.getClass())
    return false;
    final PerfilUsuario other = (PerfilUsuario) obj;
    if (codigoperfil == null) {
    if (other.codigoperfil != null)
    return false;
    } else if (!codigoperfil.equals(other.codigoperfil))
    return false;
    return true;
    }

    }

    controlUsuario—>

    package com.NASeguranca.Controller;

    import java.util.LinkedList;
    import java.util.List;

    import javax.faces.context.FacesContext;
    import javax.faces.model.DataModel;
    import javax.faces.model.ListDataModel;
    import javax.faces.model.SelectItem;
    import javax.persistence.Query;

    import org.hibernate.Session;

    import com.NASeguranca.Dao.DaoEstado;
    import com.NASeguranca.Dao.DaoPerfilUsuario;
    import com.NASeguranca.Dao.DaoUsuario;
    import com.NASeguranca.Idao.IDaoEstado;
    import com.NASeguranca.Idao.IDaoPerfilUsuario;
    import com.NASeguranca.Idao.IDaoUsuario;
    import com.NASeguranca.Model.Estado;
    import com.NASeguranca.Model.PerfilUsuario;
    import com.NASeguranca.Model.Usuario;
    import com.NASeguranca.Util.HibernateUtil;

    public class ControlUsuario {

    private DataModel model;
    private Usuario usuario = new Usuario();
    private SelectItem selectedItem = new SelectItem();
    // private PerfilUsuario selectedItem;
    public List lista = new LinkedList();

    public ControlUsuario(){}

    public String novo() {
    usuario = new Usuario();
    return “novoUsuario”;
    }

    public Usuario getUsuario() {
    return usuario;
    }

    public void setUsuario(Usuario objeto) {
    this.usuario = objeto;
    }

    public DataModel getTodos() {

    IDaoUsuario ldao = new DaoUsuario();
    model = new ListDataModel(ldao.ListarTodos());
    return model;

    }
    public DataModel getTodosUsuario(){

    IDaoUsuario ldao = new DaoUsuario();
    model = new ListDataModel(ldao.ListarTodos());
    return model;

    }
    public Usuario getUsuarioFromEditOrDelete() {
    Usuario objeto = (Usuario) model.getRowData();
    return objeto;

    }

    public String editar() {
    Usuario usuario = getUsuarioFromEditOrDelete();
    setUsuario(usuario);
    return “editarUsuario”;

    }

    public String update() {
    IDaoUsuario ldao = new DaoUsuario();
    ldao.atualizar(usuario);
    return “sucessoUsuario”;
    }

    public String excluir() {
    IDaoUsuario ldao = new DaoUsuario();
    Usuario usuario = getUsuarioFromEditOrDelete();
    ldao.excluir(usuario);
    return “sucessoDeleteUsuario”;

    }

    public String create() {
    IDaoUsuario ldao = new DaoUsuario();

    ldao.salvar(usuario);

    return “sucesso_incUsuario”;
    }

    // adicionar os itens abaixo como extras
    private String keysearch;

    public String getKeysearch() {
    return keysearch;
    }

    public void setKeysearch(String keysearch) {
    this.keysearch = keysearch;
    }

    public DataModel getTodaskeysearch() {
    IDaoUsuario idao = new DaoUsuario();
    model = new ListDataModel(idao.consultar(keysearch));
    return model;

    }

    public DataModel getTodosComNome() {
    IDaoUsuario idao = new DaoUsuario();
    model = new ListDataModel(idao.ListarTodos());
    return model;

    }

    public javax.faces.model.DataModel getModel() {
    return model;
    }

    public void setModel(javax.faces.model.DataModel model) {
    this.model = model;
    }

    public List getLista() throws Exception {
    List lista = new LinkedList();
    PerfilUsuario perfil = new PerfilUsuario();
    IDaoPerfilUsuario idao = new DaoPerfilUsuario();

    List resultado = (List) idao.ListarTudo();
    for(PerfilUsuario teste : resultado){

    SelectItem item = new SelectItem(teste.getCodigoperfil(),teste.getDescricao());
    lista.add(item);

    }
    return lista;
    }

    public PerfilUsuario Listar(String value) {
    // TODO Auto-generated method stub
    Session session = HibernateUtil.getInstance();
    PerfilUsuario list =
    (PerfilUsuario) session.createQuery(“from PerfilUsuario where codigoperfil= :value”).uniqueResult();

    return list;
    // return (PerfilUsuario) list;

    }

    /* public SelectItem getChangeValue() {
    return changeValue;
    }

    public void setChangeValue(SelectItem changeValue) {
    this.changeValue = changeValue;
    }
    */

    public void setLista(List lista) {
    this.lista = lista;
    }

    public SelectItem getSelectedItem() {
    return selectedItem;
    }

    public void SelectItem(SelectItem selectedItem) {
    this.selectedItem = selectedItem;
    }

    }

    e o daoUsuario—>

    package com.NASeguranca.Dao;

    import java.util.List;

    import org.hibernate.HibernateException;
    import org.hibernate.Query;
    import org.hibernate.Session;
    import org.hibernate.Transaction;

    import com.NASeguranca.Idao.IDaoUsuario;
    import com.NASeguranca.Model.Cliente;
    import com.NASeguranca.Model.PerfilUsuario;
    import com.NASeguranca.Model.Usuario;
    import com.NASeguranca.Util.HibernateUtil;

    public class DaoUsuario implements IDaoUsuario {

    private Session session;

    public void atualizar(Usuario objeto) {

    session = HibernateUtil.getInstance();
    Transaction tx = null;

    try {
    tx= session.beginTransaction();
    session.update(objeto);
    tx.commit();
    } catch (HibernateException e) {
    e.printStackTrace();
    tx.rollback();
    } finally {
    session.close();
    }

    }

    public List consultar(String descricao) {
    // TODO Auto-generated method stub
    session = HibernateUtil.getInstance();
    Query query = session.createQuery(
    “from Usuario l where l.descricao like :descr” );
    List list = query.setString(“descr”, “%”+descricao+”%”).list();

    return list;

    }

    public void excluir(Usuario objeto) {

    session = HibernateUtil.getInstance();
    Transaction tx = null;

    try {
    tx = session.beginTransaction();
    session.delete(objeto);
    tx.commit();
    } catch (HibernateException e) {
    e.printStackTrace();
    tx.rollback();
    } finally {
    session.close();
    }
    }

    public void salvar(Usuario objeto) {
    session = HibernateUtil.getInstance();
    Transaction tx = null;

    try {
    tx = session.beginTransaction();
    session.save(objeto);
    tx.commit();
    } catch (HibernateException e) {
    e.printStackTrace();
    tx.rollback();
    } finally {
    session.close();
    }

    }

    public List ListarTodos() {
    // TODO Auto-generated method stub

    session = HibernateUtil.getInstance();
    List list = session.createQuery(“from Usuario”).list();
    return list;

    }

    }

    meu conversor —->

    package com.NASeguranca.Util;

    import java.util.Map;

    import javax.faces.component.UIComponent;
    import javax.faces.context.FacesContext;
    import javax.faces.convert.Converter;

    import com.NASeguranca.Model.PerfilUsuario;

    public class GenericConverter implements Converter{
    public Object getAsObject(FacesContext context, UIComponent
    component, String value) {
    if (value != null) {
    System.out.println(value+” esse aqui é o super valor”);
    return getAttributes(component).get(value);
    }else{
    System.out.println(“tudo errado”);
    }
    return null;
    }

    public String getAsString(FacesContext context, UIComponent
    component, Object value) {
    if (value instanceof PerfilUsuario) {
    System.out.println(value.getClass());
    System.out.println(“3”);
    PerfilUsuario vo = (PerfilUsuario) value;
    getAttributes(component).put(String.valueOf(vo.getCodigoperfil()),
    vo);
    return String.valueOf(vo.getCodigoperfil());
    }

    return “0”;
    }

    private Map getAttributes(UIComponent component) {
    return component.getAttributes();
    }

    }

    ja tentei de td mas nao consegui vc pode me ajudar?
    há desculpe por postar este monte de código aqui, abraços.

  5. Olá Marcia, fica complicado ler essa enorme quantidade de código, eu até tentei, mas fica complicado 🙂

    Deixa eu ver se entendi, você montou uma combo-box com objetos do tipo PerfilUsuario, certo? E quando você seleciona você quer que o valor selecionado seja do tipo Usuário? É isso?

    Abraços.

  6. Opa Ponte, tudo beleza? A pergunta é, e qual seria uma boa solução quando preciso criar options dinâmicos através de javascript.

  7. Bem, ao se trabalhar com JSF tem que ter em mente que o que importa é o estado dos componentes no servidor, o que você faz no cliente (como criar inputs dinamicos, hiddens, combo-boxes etc) não refletem na árvore de componentes do JSF. Então para criar options dinâmicas para teu componente h:selectOneMenu por exemplo você teria que alterar o estado deste componente também no servidor. Uma solução seria através do Ajax4jsf, assim depois de inserido um novo item você reRenderizaria o componente.

    Alguns componentes como o t:pickList do Tomahawk consegue fazer isso no cliente (browser) pois o componente foi preparado para isso.

    Outra maneira, mas não a recomendada seria obter os valores através do proprio request, porém assim você estaria abrindo mão das vantagens do JSF.

    Enfim, existem algumas soluções, outras simples e outras mais “fora do convencional com JSF”, escolher a melhor depende da tua necessidade e prazo 🙂

    Mas por que você iria querer gerar options dinâmicos?

  8. Rapaz, estamos desenvolvendo um sistema com diversos casos de uso não muito convencionais. Na verdade eu vou ter diversos combos com options dinâmicos, pois o caso de uso é um mestre detalhe com sub detalhe, os valores do combo vão está no intervalo de 0..Maior número digitado em vários inputText. Ainda ontem resolvi dessa maneira: ao digitar o número em algum desses inputText é feito uma chamada ao um método no servidor que verifica se o valor digitado é maior do que os demais e depois é renderizado os combos.

    Valeu

  9. Rafael, parabéns! Seu blog foi o único site que eu encontrei a solução para este problema. Em nenhum outro atentaram para fato de que o problema estava em como funciona o método equals().

  10. POxa, tô começando JSF eu meio q tô boiando em Converter…..

    Teria como voce me mandar por e-mail um exemplo só pra eu ver como é a implementação….

  11. Olá vc poderia mostrar um exemplo da utilizacao desse selectOneMenu com a tag saveState do tomahawk, nao sei como resolver o problema usando essa tag. Apesar de ter encontrado vários posts dizendo isso.
    Sou iniciante em JSF e tenho uma nocao pequena da sintaxe e do funcionamento do framework.
    Obrigada!

  12. Cara, após 3 dias apanhando com esse problema, encontrei o seu post através do Google.

    Em 5 minutos resolvi o problema.

    Excelente post !

  13. Cara muito bom mesmo, mas uma coisa você pode me dar uma dica de como vou sobre-escrever os dois métodos sou novo em JSF e Java

  14. Olá Rafael,
    Estou efetuando alguns testes com JSF 2.0, com selectManyListbox insiste em retornar Validation Error: Value is not valid já com selectOneMenu funciona perfeitamente. Implementei o converter e tb os métodos toString, hashCode e equals. Tens idéia do que pode ser?

    obrigado.

  15. Olá Ezequiel,

    Levando em consideração que seu converter esteja corretamente implementado então de duas uma:
    1) Ou seus métodos equals e hashCode não estão corretos;
    2) Ou sua lista [a lista utilizada para popular o componente] está se perdendo durante a submissão do formulário – neste caso, em que escopo está teu managed bean?

    Abraços e boa sorte.

    PS.: Aconselho-te a participar da lista de discussão da #javasf, lá com certeza você obterá feedback bem mais rápido do que aqui.

  16. Olá Rafael, obrigado pelo retorno.

    Se puder dar uma olhadinha, nesse link contém o código da classe com os métodos equals e hashCode ( http://www.tomazinisistemas.com.br/testejava/RequestsTiRequests.txt).

    O escopo está como @SessionScoped.

    Nesse link tem o converter (www.tomazinisistemas.com.br/testejava/RequestsTiAttendantsTypeConverter.txt

    Obrigado pela dica, vou participar da lista de discussão.

    obs: Recuperar a lista selecionada com um List eu consegui, mas li acho que foi aqui em seu blog mesmo que o correto é trabalhar diretamente com os objetos, por isso queria recuperar os objetos e não apenas o ID/Código deles.

    muito obrigado.

  17. Ezequiel,

    Suas classes parecem corretas. Agora te pergunto: Foi você quem escreveu esses métodos equals() e hashCode() ou eles foram gerados pela IDE? Eu aconselho que você os gere pela IDE, bem mais prático e seguro para 99% das vezes 🙂

    Eu não sei se você entendeu a razão do segundo problema que comentei anteriormente, espero que sim. Se tiver dúvidas é só perguntar.

    Sobre sua “obs” eu saliento em dizer que trabalhar com objetos ao invés de ID/Código não é que seja _O_ correto, mas sim o mais próximo do ideal. Na maioria dos casos trabalhar com objetos facilitará e muito a tua vida, principalmente se você estiver utilizando alguma framework ORM como JPA/Hibernate.

  18. Blza Rafael,

    Gerei no Netbeans.

    Muito obrigado, abraços.

  19. Rafael, tive o mesmo problema com um selectOneMenu dinâmico.
    No meu caso, resolvi usando a4j para rerenderizar o meu componente e usando a tag .
    Abraços

  20. ops, não apareceu a tag… é a a4j keepalive.

  21. Olá Rafael,

    Gostei do arquivo tenho meus hascode e equals,

    em State.java

    public boolean equals(Object o) {
    if(this == o) return true;
    if(!(o instanceof State))
    return false;
    State p = (State ) o;
    if(!this.country.equals(p.getCountry())) return false;
    return super.equals(o);
    }

    public int hashCode() {
    int h = 31;
    h = 31*h + super.hashCode();
    h = 31*h + this.getCountry().hashCode();
    return h;
    }

    em Town. java substitui no lugar do Country em State.java é State.

    minha situação é o seguinte, tenho um suggestion box no seam (City) aninhado com dois combobox (State e Country). Quando seleciono a cidade por exemplo Salvador no suggestion box, automaticamente o state no combobox é marcado e country como Bahia-Brazil respectivamente. Até ai sem problemas, mas quando submeto o formulario e alguns campos são requeridos ele não processa o meu método, claro. Então no campo state, apenas nele, aparece a msg Value is not valid e o combobox vem vazio. Mas Country vem selecionado como Brazil ainda e o suggestion box como Salvador.

    A outra situação que me ocorre tbm é ao digitar cidade de outro país e estado, por exemplo Michigan, ele não reRender novamente os campos. Pode me dar uma ajudinha, por favor com isso?

  22. Valeu Rafael,

    O JSF bem que poderia mostrar uma mensagem mais amigável… fica difícil deduzir que o problema é a falta de equals e hashCode. Ainda bem que tem o teu post bem colocado no google.

    Abraço.

  23. Olá Ponte,
    Seguinte me surgiu uma gigantesca dúvida sobre os converters.

    Os converters não aceitam Injeção de dependencia Spring ou EJB?
    Pois gostaria de injetar uma classe de persistência para buscar um determinado objeto.
    Você pode me esclarecer isso?

    Thank’s

  24. Rodrigo,

    Por padão não. Ele não aceita DI nem por Spring nem por EJB. Contudo, se estiver trabalhando com JBoss Seam será possível ter DI funcionando como você gostaria!

    No entanto, pensando agora, é possível passar o componente (do Spring) para o converter ou validator pela página via EL (f:attribute, por exemplo) ou simplesmente avaliar a EL programaticamente atráves do FacesContext dentro do converter.

    Esse post pode te ajudar,
    http://www.rponte.com.br/2008/02/24/aproveitando-os-beans-do-spring-em-suas-paginas-jsf/

    Abraços e boa sorte.

  25. Muito bom post, Rafael.
    Cheguei a este post porque também tive um problema com Select One que gerava um erro de validacao. Porém nao uso converter. Entao complicou, e não me ajudou muito seu post, não desmerecendo.

    Em resumo o que ocorria eh que em minha aplicacao, a cada atribuição de valor do bean, o Bean era criado, nao o mantinha. Com isso qdo selecionava o selectOne ele executava o metodo init() e bugava tudo.

    Para resolver o problema coloquei no .xhtml a tag

    .
    .
    .

    Dessa forma resolveu meu problema.
    Parabéns pelo Post.

  26. Heber,

    Se eu entendi de fato o seu problema, então ele não é muito diferente do que foi explicado no post, contudo está mais relacionado ao escopo conversacional e como a árvore de componentes é reconstruída pelo Faces do que a estrutura interna das tuas entidades (objetos).

    Se o Jsf não consegue reconstruir a árvore de componentes com o estado anterior (ou seja, com a mesma lista de objetos utilizada para gerar a combo) então você encontra o mesmo problema de “como o usuário conseguiu selecionar um item se ele não existe na minha lista de opções?”.

    Enfim, os dois problemas são bem comuns mesmo!

    Um abraço e valeu pelo comentário.

  27. É isso mesmo, mas a solução para esse problema seria manter o scopo do bean, até que por meio de uma ação do usuário, ou fluxo do sistema, o bean seja “destruído”.

    Mas está correlacionado, seu post me ajudou a fazer um debug, pois como não usava converter, nao precisei fazer override dos metodos equals(obj) e hascode().

    Mas uma vez, valeu!

  28. Pessoal,
    Li e reli esse texto mais de 30 vezes. rs Segui passo a passo mas não consigo resolver o problema. Todos aqui conseguiram resolver sobrescrevendo os métodos hasCode e equals ?

    Já estou há vários dias nesse mesmo problema. Percebi que se eu colocar o meu bean em SessionScoped funciona direitinho, mas acho que isso não é o certo. Certo ? rs

    Abraços

  29. Ótimo post,

    Explicação de forma clara, muito útil.

    Parabéns.

  30. Caro Rafael,

    Antes de tudo gostaria de agradecer por compartilhar conosco seu tempo, conhecimento e entusiamo, tornando a vida de novatos em java, como eu, menos desesperadora.
    Há algum tempo tenho feito uso do seu blog para esclarecer várias das minha dúvidas, porém estou parado em um problema que até o presente momento não encontrei forma de resolver.

    Tenho um SelectOneMenu que recebe uma lista de SelectItem (LinkedList) conforme abaixo:

    XHTML

    BEAN

    public List getTipoEventList() {
    List toReturn = new LinkedList();
    for (TipoEvent i : tipoEventDAO.getTipoEventAll()) {
    toReturn.add(new SelectItem(i, i.getNmTipoEvent()));
    }
    return toReturn;
    }

    Ocorre um erro quando tento acessar/atribuir um valor a entidade:

    0/11/2010 01:18:20 com.sun.faces.lifecycle.RenderResponsePhase execute
    INFO: WARNING: FacesMessage(s) have been enqueued, but may not have been displayed.
    sourceId=j_id79:j_id86[severity=(ERROR 2), summary=(Conversion Error setting value ‘org.app.sgv.persistence.entity.TipoEvent@80’ for ‘null Converter’. ), detail=(Conversion Error setting value ‘org.app.sgv.persistence.entity.TipoEvent@80’ for ‘null Converter’. )]

    Obs.: Este exemplo eu peguei de uma vídeo aula, achei interessante porque não utilizava conversores, era mais prático…. é realmente possível não utilizar conversores dessa forma?

  31. Caro Rafael,

    Antes de tudo, gostaria de agradecer por compartilhar conosco seu tempo, conhecimento e entusiamo, tornando a vida de novatos em java, como eu, menos desesperadora.
    Há algum tempo tenho feito uso do seu blog para esclarecer várias das minha dúvidas, porém estou parado em um problema que até o presente momento não encontrei forma de resolver.

    Tenho um SelectOneMenu que recebe uma lista de SelectItem (LinkedList) conforme abaixo:

    XHTML

    BEAN

    public List getTipoEventList() {
    List toReturn = new LinkedList();
    for (TipoEvent i : tipoEventDAO.getTipoEventAll()) {
    toReturn.add(new SelectItem(i, i.getNmTipoEvent()));
    }
    return toReturn;
    }

    Ocorre um erro quando tento acessar/atribuir um valor a entidade:

    0/11/2010 01:18:20 com.sun.faces.lifecycle.RenderResponsePhase execute
    INFO: WARNING: FacesMessage(s) have been enqueued, but may not have been displayed.
    sourceId=j_id79:j_id86[severity=(ERROR 2), summary=(Conversion Error setting value ‘org.app.sgv.persistence.entity.TipoEvent@80’ for ‘null Converter’. ), detail=(Conversion Error setting value ‘org.app.sgv.persistence.entity.TipoEvent@80’ for ‘null Converter’. )]

    Obs.: Este exemplo eu peguei de uma vídeo aula, achei interessante porque não utilizava conversores, era mais prático…. é realmente possível não utilizar conversores dessa forma?

  32. Opa,
    Faltou lembrar que as vezes é necessário colocar o ” para manter o objeto submitado na request :)…..me bati um tempão com isso.

  33. Olá Rafael.
    Implementei tudo (eu acho) corretamente e funcionou \o/.
    Porém… se eu faço um componente composto (composite component) e passo o array de SelectItem para este componente como sendo um parâmetro (ou seja, um cc:attribute no componente), eu não consegui entender ainda.. mas ele explode um “Validation Error: Value is not valid”.
    =(
    Passo uma entidade pro construtor do SelectItem. Essa entidade tem converter tudo certinho. Tbm tem hashCode e equals. O problema é quando eu passo pro composite mesmo.. ai explode erro. Fora desse composite ele funciona normal T_T.
    Se vc descobrir o pq desse comportamento eu ficaria mt grato.
    =)
    Até.

  34. Disculpa eu vim aqui de novo… mas eu fiz um teste e não passei “por parâmetro” o array. Coloquei a chama do bean direto no meu composite component e mesmo assim deu erro o.O.

  35. Olá Guilherme,

    Eu teria que olhar o problema mais de perto, examinar o código etc. Mas acredito que seja apenas algum detalhe que você tenha esquecido.

    No mais, aconselho-te a ler os comentários nesse post pois este problema pode acontecer em outros determinados cenários. Daí existem algumas dicas nos comentários que poderão te dar alguma luz.

    No mais, aconselho-te a perguntar no grupo #javasf, pois lá com certeza você terá um feedback bem mais rápido e provavelmente preciso do teu problema.

    Um abraço.

  36. Olá Rafael,

    Muito legal seu post, graças a ele consegui entender melhor o que se passava no meu programa que usa um selectManyListbox, o código abaixo ilustra a situação:

    Então esse produtoCollection é uma relação ManyToMany entre Acomodação e Produto, ai assim uso o selectItems que me retorna uma lista com id e valor, quando eu submetia o formulário gerava o erro, mas isso pq eu não tinha o converter identificado, como já tinha o hash, equals e converter criados foi preciso apenas adicionar o value da notação (se utilizar jsf1.2 basta criar um converter em faces-config.xml com o id que será utilizado no selectManyListbox) da seguinte forma:
    @FacesConverter(forClass = Produto.class, value=”br.com.converter.Produto”)
    public static class ProdutoControllerConverter implements Converter { … }

    Com isso a lista sabe qual é o converter a utilizar, o JSF só não sabe pois o mesmo não identifica o objeto da lista destino para então aplicar o converter sobre tal objeto.

    Estou procurando entender poque não o faria, já que o mesmo está identificado e não é um genérico.

  37. Cortou meu código:
    h:selectManyListbox id=”produtoCollection” value=”#acomodacaoController.selected.produtoCollection}” title=”#{bundle.EditAcomodacaoTitle_produtos}” converter=”br.com.converter.Produto”>

    </h:selectManyListbox

  38. Faltou mais isso
    f:selectItems value=”#{produtoController.itemsAvailableSelectMany}”/

  39. Olá RPonte, agradeço pelas suas dicas e ajuda.

    Seguinte, eu tive esse problema em minha aplicação. Possuo o converte, funcionando perfeitamente, e tenho os métodos equals e hash sobrescritos no meu bean. Mesmo assim o problema de validação ocorria.
    Debuguei e notei que na hora de verificar o equals, um dos objetos estava indo null na comparação, o que gerava o erro.

    O motivo era que ele estava vindo como Lazy do banco de dados. Eu mudei o objeto no Bean para Eager e o erro não aconteceu mais. Então, que sirva de possível solução para aqueles que possuem o erro mas cujas verificações de código estejam feitas de forma correta.

    Meu objetivo agora, é descobrir a razão do objeto não estar vindo quando eu o chamo, mesmo ele estando lazy.

    []’s

  40. Rafael,
    Não sei se tem a ver com o post aqui, mas estou com um problema de Property ‘id’ not writable on type java.io.Serializable.

    Minha entidade realmente tem um getId() que retorna um Integer, porém implementa uma interface da seguinte maneira:

    public interface Entidade extends Serializable {

    public T getId();
    }

    Ao colocar algum valor num inputText qualquer que possua a entidade.id e depois dar um reRender, recebo essa exception (Property ‘id’ not writable on type java.io.Serializable).

    Sabe o que pode estar ocorrendo ?
    Desculpe se a dúvida for muito boba… 😛

    Abraços e agradeço a ajuda!

  41. Oi Rafael,
    muito bom esse post!!! Graças a pessoas como você que cada vez mais gosto de java. A comunidade é bem unida.
    Com esse post, resolvi um problema que já vinha tenha a 4 dias e já conversado com umas 3 pessoas diferentes.
    abcs

  42. Somente uma observação que eu encontrei em um projeto que eu estava trabalhando, nos utilizavamos Jboss Seam 2.2.
    Além de sobrescrever os metodos equals e hascode. Tinha outro problema dificil de indentificar, quando você pede o eclipse/netbeans para gerar esse dois metodos equals/hascode ele gera da seguinte maneira:

    Exemplo:
    public boolean equals(Object obj) {
    if (obj == null)
    return false;
    if (getClass() != obj.getClass())
    return false;
    final RegionImpl other = (RegionImpl) obj;
    if (id == null) {
    if (other.id != null)
    return false;
    } else if (!id.equals(other.id))
    return false;
    return true;
    }

    **** MUITO CUIDADO COM ESSE CODIGO: other.id pois isso gerava um problema para mim. Para resolver trocamos para esse codigo: other.getId()

    Exemplo:
    public boolean equals(Object obj) {
    if (obj == null)
    return false;
    if (getClass() != obj.getClass())
    return false;
    final RegionImpl other = (RegionImpl) obj;
    if (id == null) {
    if (other.getId() != null)
    return false;
    } else if (!id.equals(other.getId()))
    return false;
    return true;
    }

    Gastamos mais de dias para descobrir isso, estou postando somente para ajudar a galera ai.

  43. Oi Rafael. Primeiramente gostaria de parabenizá-lo pelo post, muito bom e simples. Estou com esse problema, já implementei o equals() e hashCode() mas o problema persiste. Em meu cenário, eu tenho um selectOneMenu que é atualizado de acordo com um outro valor selecionado. Existe em minha página um p:autoComplete. Esse autoComplete atualiza um valor no MB e dentro dele um tenhum uma tag p:ajax com o atributo update setado para o id desse meu selectOneMenu, ou seja, quando o usuário escolher no p:autoComplete, a lista de itens do selectOneMenu será refeita. Isso pode estar influenciando nesse erro de validação? Desde já agradeço.

  44. Muito obrigado! Estava começando a ficar estressado com este erro.

  45. Ponte,
    Muito boa a matéria, mas no meu caso não funcionou ainda. O negócio é o seguinte: eu já implementei o converter e já mandei o netbeans gerar o equals() e o hashCode(), mas continua dando pau na validação. Na saída do converter(getAsObject()) tá tudo ok, mas ao debugar o equals(), percebi de que o parâmetro Object está sendo null. Meus MB’s estão como RequestScoped e ao mudar o escopo começa a pipocar erro no atributo var de um p:autocomplete.
    Bom, expliquei o erro mas vou explicar o contexto: Tenho um formulário que tenho um autocomplete para entra com o sistema e ao preencher este é alimentado um combo (p:SelectOneMenu) com as versões deste sistema, onde o usuário deve selecionar a versão desejada. Pois bem, ao clicar no botão para gravar é apresentado o erro de validação…

    Não sei mais o que fazer, já estou perdendo o resto dos cabelos (rsrsrs).

    Valeu

  46. Olá Felipe, tudo bom?

    Bem, levando em consideração que você seguiu todos os passos para garantir que seu converter e entidade estejam corretos, eu poderia supor que, baseado no seu cenário, seu problema está relacionado ao escopo de conversação.

    Por via das dúvidas faça assim: garanta que a lista dinâmica que é gerada para a sua combo-box esteja em um escopo maior que request (view ou session). Depois disso tente submeter novamente seu formulário. Na pior da hipoteses coloque todo o managed bean em escopo de session.

    Enfim, dê uma lida nos demais comentários deste post que você encontrará a explicação sobre o porquê do escopo conversacional está influenciando no seu cenário.

  47. Bom dia, Rafael!

    Bom, seguindo as suas dicas alterei os escopos e tal, mas agora estou enfrentado o problema abaixo:

    AVISO: StandardWrapperValve[Faces Servlet]: PWC1406: Servlet.service() for servlet Faces Servlet threw exception
    javax.el.PropertyNotFoundException: /faces/branchcadastro.xhtml @31,63 itemLabel="#{sist.codigo} - #{sist.sigla} - #{sist.nome}": The class 'java.lang.String' does not have the property 'codigo'.
    

    Como pode acontecer isso se “sist” está declarado em var?

                            
                                
                            
                            
                            
                                
                                
                            
    
    
  48. Agora acho que vai:

    p:autoComplete id=”sistema” value=”#{branchBean.sistema}”
    size=”80″
    required=”true” requiredMessage=”Obrigatório informar o Sistema!”
    completeMethod=”#{sistemaBean.completaSistema}” var=”sist”
    itemLabel=”#{sist.codigo} – #{sist.sigla} – #{sist.nome}” itemValue=”#{sist}”
    converter=”sistemaConverter”
    maxResults=”10″
    forceSelection=”true”

  49. Olá Felipe,

    Sinceramente não sei, eu teria que olhar bem mais de perto o seu código. Eu aconselharia você revisar novamente o código dos managed beans e ver se existe algo errado ou no mínimo incoerente. Ou, talvez, possa ser algum problema no componente.

    No mais, te aconselho a participar do grupo #javasf, lá com toda certeza alguém poderá te ajudar com relação ao uso deste componente.

  50. Opa Rafael! Desculpa a demora, pois estava fora. Cara, era mancada minha, pois o atributo no managed bean estava como String e o converter passava a classe. Setando como classe também no managed bean tornou a funcionar.

  51. cara ja tem 2 dias que tou apanhando nisso…. alguma sugestão?

  52. Muito Obrigado, Muito bom seu post… parabéns;

  53. Meus parabéns pelo post.

    Realmente significante e satisfatório.

    Contudo estou com algo até agora insolúvel para mim, como estou começando com JSF. Tenho um classe Cliente e com todas as regalias que se pede para uma manipulação para cadastro com Hibernate e JSF, nos primeiros dias em que realizei o CRUD, funcionou 100%, mesmo com os 3 SelectOneMenu na tela. Agora, inesperadamente está me retornando o Erro de Validação.

    O que pode ser, o que intriga-me é o fato de dantes funcionar e agora, sem ter feito nada, retorna-me este erro.

    Obrigado pela ajuda.

  54. Olá Valesy,

    É bem estranho algo estar funcionando e simplesmente do nada parar de funcionar. Você tem certeza que não modificou nada no código ou testou a funcionalidade corretamente?

    Em qual componente exatamente ocorre erro de validação?

    Se possível entre no grupo #javasf, lá o pessoal poderá te ajudar e analisar seu código mais rapidamente do que eu por aqui.

  55. Obrigado vou fazer isso.
    O componente é o SelectOneMenu. É estranho, juntamente com um amigo meu, tetamos ver o erro, porém, não encontramos. Eu uso o eclipse e, no console não me apresenta nenhum apontamento para um possível, e o que me deixa mais sem saber por onde começar é que, em lugar nenhum fiz algo para validação do campo, nem o required=”true”, estou usando.

    Obrigado pela atenção.

    Valesy Moreira

  56. Oi Valesy,

    Normalmente quando trabalhamos com combos aninhadas no JSF e não definimos corretamente o escopo do managed bean ou mesmo dos objetos utilizados nos componentes de combo pode ocorrer este erro de validação.

    Para ter certeza que o problema é ou não de escopo de conversação você pode colocar seu managed bean em escopo de session e verificar se o problema ainda continua ou não.

    No mais, nos comentários do meu post sobre Entity Converters você pode encontrar uma discussão sobre o assunto: http://www.rponte.com.br/2008/07/26/entity-converters-pra-da-e-vender/

  57. Parabéns! direto ao ponto, claro e objetivo!

  58. Muito bom! Segui as dicas e solucionei o problema, a dica do Herick, sobre o Eager foi o “X” da questão.

    Parabéns! muito bom mesmo.

  59. Excelente post!!! Já estava a dois dias tentando resolver esse problema.

  60. Estou com o mesmo problema! Porém mesmo colocando os hashcodes e o equals continua aparecendo a mensagem “Erro de conversão ao definir o valor ‘2’ para ‘null Converter’.”

    Alguém me ajuda por favor!!!!

  61. O erro “null Converter” acontece quando seu componente não sabe como converter o valor entrado pelo usuário em um objeto de negócio seu.

    Você associou um converter no componente?

  62. Olá Rafael! Antes de tudo gostaria de parabenizá-lo pelo post. Iniciado em 2008 e ainda com participantes até hoje (incluindo eu :). Bem, tive problemas para fazer essa solução funcionar, mas percebi que em meu caso foi problema de cache. Mas tive uma dúvida: essa solução acaba indo de cara com uma recomendação do JSF 2: não é recomendado sobrepor os métodos equals e hashCode. É lógico, que o que é recomendado, não quer dizer que não possa ser feito. Bem, mas como tenho pouca experiência com JSF (estou estudando a pouco tempo), para este problema reportado aqui, existe outra alternativa? (já havia armazenado no selectOneMenu o id da entidade ao invés do objeto, mas é óbvil que a solução apresentada aqui é mais racional). E como você sugere a implementção dos métodos equals e hashCode? Acabei usando a implementção gerada automaticamente pelo eclipse, e só utilizei o ID para gerar tais métodos. Acabei vendo em alguns dos comentários aqui que alguem teve problemas com esta solução, sendo necessário ele fazer uma modificação.

  63. Oi Israel,

    Fico feliz que esse post tenha te ajudado de alguma forma.

    Onde você leu sobre esta recomendação “essa solução acaba indo de cara com uma recomendação do JSF 2: não é recomendado sobrepor os métodos equals e hashCode”? Sinceramente ela não faz muito sentido para mim, talvez porque eu esteja fora do contexto do post/artigo que você leu.

    Esta solução normalmente é interessante quando você quer trabalhar diretamente com as entidades e não ids. Eu prefiro utiliza-la pois facilita muito o meu trabalho. Mas em alguns casos utilizar ids pode ser até mais simples e não faz muito sentido utilizar a entidade inteira, principalmente se ela for muito complexa e grande.

    Os problemas mais comuns com essa abordagem estão relacionados a implementação dos métodos equals() e hashCode() e escopo de conversação. Se você implementou corretamente os métodos e ainda assim está tendo este problema do post então é provável que esteja relacionado ao escopo de conversação que está muito pequeno.

    No mais, se eu puder te ajudar em mais alguma coisa é só falar.

  64. Boa noite Rafael! Valeu pela resposta. Ah, me enganei ao dizer JSF, e era JPA. Bem, quando estive procurando por esta solução apresentada aqui, li um monte de coisas. Acho que li algumas recomendações, incluindo os métodos que citei. Bem, mas acredito que tenha “assimilado” algo errado, sem perceber. Digo isso, pois percebi que achou estranho a afirmação que fiz. Bem, irei procurar novamente sobre estas recomendações e retorno. Mais uma vez, obrigado.

  65. Olá Israel,

    Claro que eu posso ajudar sim, mas te adianto que minha experiência com IceFaces é quase zero.

    Talvez eu demore a te responder devido a vida corrida que tenho hoje em dia. Logo, te aconselho a entrar no grupo #javasf (https://groups.google.com/forum/#!forum/javasf) e enviar tuas dúvidas e questionamentos para lá. Com certeza o pessoal tem bem mais experiência do que eu com IceFaces e eles poderão te responder quase que prontamente.

    No mais, tentarei te ajudar o máximo que eu puder.

  66. Olá Rafael!
    Seu post me ajudou muito! Valeu!
    Abs

  67. Otimo post! Passei por esse erro vc me ajudou valeu!

  68. “como você não sobreescreveu os métodos equals() e hashCode() da classe Municipio”

    Rafael, quando eu li essa frase, rachei de rir sozinho aqui cara. Parece que tudo ficou tão óbvio de uma hora pra outra rsrs, realmente foi um insight!…

    Cara, ótima postagem!!!! Valeu mesmo!

  69. Parabéns Rafael
    Salvou a Patria.

  70. Parabéns pelo Artigo.

    Muito bem explicado e uma ótima solução!

  71. Putz,
    detalhinho FDP que faz o cara querer morder a nuca buscando o erro.. rsrs.

    Obrigadão Rafael, dica valiosa.

  72. Muito bom o post eu tava com esse problema faziam tres dias olhando e procurando onde estava meu erro valeu!!

  73. Olá, estou desenvolvendo um ecommerce e mesmo aplicando essa solução continuo com o erro. Poderia me ajudar?

  74. Oi Joelmir,

    Qual o escopo do seu managed bean? Uma boa maneira de saber se o problema tem a ver com o escopo do managed bean é alterando o escopo para @SessionScoped ou mesmo @ViewScoped.

    Um abraço,

  75. Olá,

    Tive o mesmo problema, mas o erro estava no método Equals gerado pelo Eclipse, pois estou usando ID. Tive que utilizar o método “other.getId()” pra forçar ele a pegar o valor que por algum motivo estava null.

    Segue o método correto:

    @Override
    public boolean equals(Object obj) {
    if (this == obj)
    return true;
    if (obj == null)
    return false;
    if (!(obj instanceof Pessoa))
    return false;
    Pessoa other = (Pessoa) obj;
    if (idPessoa == null) {
    if (other.idPessoa != null)
    return false;
    } else if (!idPessoa.equals(other.getId()))
    return false;
    return true;
    }

  76. O post é antigo mas continua valendo. Estava parado há alguns anos e estou tentando implementar um sistema pra minha empresa, tive problemas em utilizar a navegação com Tab no autocomplete do primefaces e resolvi trocar pelo selectonemenu, mas ele só funcionava uma vez!!! Quando tentava mudar pela segunda vez dava o erro citado no seu post.

    Enfim, implementei os métodos hashcode e equal pelo gerador automático do ide e pronto, funcionou perfeitamente. Vlw!!!

Deixe um comentário