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 thevalidproperty tofalse.
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.
Tags: converter, Java, JavaServer Faces, JSF, SelectOneMenu, validator
February 1st, 2008 at 6:37 pm
Valeu Rafael…
Me salou…
February 2nd, 2008 at 3:45 am
Excelente post!! Grande Ponte, mais uma vez dando um show… Eu não sabia desde detalhe… Muito importante saber disto….
Obrigado, forte abraço!
February 24th, 2008 at 9:28 pm
[...] é que enquanto o Tarso estava fazendo os testes e implementando a solução ele caiu naquele velho probleminha do selectOneMenu, não exatamente como no cenário que eu havia blogado, mas o problema e a solução foram a mesma, [...]
February 28th, 2008 at 3:50 am
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.
February 28th, 2008 at 4:54 am
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.
February 28th, 2008 at 7:59 am
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.
March 2nd, 2008 at 7:14 am
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.
March 19th, 2008 at 2:19 pm
Opa Ponte, tudo beleza? A pergunta é, e qual seria uma boa solução quando preciso criar options dinâmicos através de javascript.
March 20th, 2008 at 5:16 am
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?
March 20th, 2008 at 7:02 am
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
April 14th, 2008 at 1:02 pm
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().
June 11th, 2008 at 5:27 am
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….
July 22nd, 2008 at 6:08 am
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!
July 25th, 2008 at 7:58 am
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 !
July 26th, 2008 at 11:16 am
[...] favor, não esqueçam de implementar os métodos equals e hashCode, evitemos cair naquele velho probleminha, [...]
March 19th, 2009 at 3:34 pm
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
June 24th, 2009 at 7:57 pm
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.
June 24th, 2009 at 8:02 pm
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.
June 25th, 2009 at 4:34 pm
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.
June 25th, 2009 at 4:53 pm
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.
June 25th, 2009 at 5:19 pm
Blza Rafael,
Gerei no Netbeans.
Muito obrigado, abraços.
July 1st, 2009 at 5:46 am
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
July 1st, 2009 at 5:47 am
ops, não apareceu a tag… é a a4j keepalive.
July 20th, 2009 at 10:11 am
tudo bem… rolou com keepAlive
August 11th, 2009 at 6:50 am
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?
January 18th, 2010 at 9:42 am
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.
May 8th, 2010 at 6:20 am
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
May 9th, 2010 at 8:50 am
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.
June 22nd, 2010 at 10:12 am
Excelente post.
Me ajudou muito, muito obrigado.
June 29th, 2010 at 7:02 am
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.
June 29th, 2010 at 7:05 am
Nao ficou visivel a tag, entao segue o link da utlizacao (tag do ajax keepalive)
http://docs.jboss.org/richfaces/latest_3_3_X/en/devguide/html/a4j_keepAlive.html
bom proveito.
June 29th, 2010 at 7:16 am
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.
June 30th, 2010 at 6:17 am
É 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!
July 25th, 2010 at 5:49 pm
Extraordinário.
July 27th, 2010 at 9:09 pm
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
August 18th, 2010 at 3:47 pm
Ótimo post,
Explicação de forma clara, muito útil.
Parabéns.
November 19th, 2010 at 9:28 pm
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?
November 19th, 2010 at 9:30 pm
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?
February 3rd, 2011 at 12:34 pm
Opa,
Faltou lembrar que as vezes é necessário colocar o ” para manter o objeto submitado na request :)…..me bati um tempão com isso.
February 22nd, 2011 at 6:48 am
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é.
February 22nd, 2011 at 7:03 am
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.
February 22nd, 2011 at 7:43 am
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.
February 24th, 2011 at 6:49 pm
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.
February 24th, 2011 at 6:50 pm
Cortou meu código:
h:selectManyListbox id=”produtoCollection” value=”#acomodacaoController.selected.produtoCollection}” title=”#{bundle.EditAcomodacaoTitle_produtos}” converter=”br.com.converter.Produto”>
</h:selectManyListbox
February 24th, 2011 at 6:51 pm
Faltou mais isso
f:selectItems value=”#{produtoController.itemsAvailableSelectMany}”/
May 10th, 2011 at 5:44 am
Esse post salvou minha vida
VLW
May 10th, 2011 at 10:05 am
Muito BOM!!!
June 8th, 2011 at 5:00 pm
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
June 22nd, 2011 at 1:41 pm
Muito Bem!
July 21st, 2011 at 8:59 am
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!
August 4th, 2011 at 8:46 am
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
August 26th, 2011 at 8:08 pm
Mais uma vida salva pelo post!!!kkkk
Valeu Rafael..força mano
November 22nd, 2011 at 12:38 pm
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.