[RESOLVIDO] EntityConverter, lista mas não seleciona corretamente

Boa tarde,

Tentei e pesquisei muito antes de estar aqui novamente trazendo mais problemas mas… não consegui mesmo.

Estou tentando popular um h:selectOneMenu com objetos do meu projeto. Para tal estou utilizando um conversor genérico que foi modificado e publicado por um membro aqui do GUJ, o Sr. Flavio Almeida.

Consigo popular normalmente, mas quando seleciono e submeto página, aparece o erro:

Erro de validação: o valor não é válido

Meus métodos equals e hashCode estão sobrescritos (gerado automaticamente pelo eclipse).

Códigos:

View:

<h:selectOneMenu value="#{bean.pessoaSelecionada}" converter="entityConverter">
        <f:selectItems value="#{bean.pessoas}" var="pessoa" 
            itemLabel="#{pessoa.nome}" itemValue="#{pessoa}" />
</h:selectOneMenu>

Bean:

[code]

private Pessoa pessoaSelecionada;

private List pessoas;

// populando a list

PessoaDao pesDao = new PessoaDao();

List lista = (List) pesDao.getAll();

pessoas = new ArrayList();

pessoas.add(new SelectItem(null, “”));

for (Pessoa pessoa : lista) {

      pessoas.add(new SelectItem(pessoa, pessoa.getNome()));  

}[/code]

Alguém tem alguma sugestão?

Obrigado!

Bom, não deve fazer diferença, mas vc usando uma lista de SelectItem, vc não precisa definir o var, o itemValue e o itemLabel no seu componente no seu XHTML. Basta ter a referência para a lista.

Cola aqui o código do converter que o Flávio fez.

[quote=Arthur F. Ferreira]Bom, não deve fazer diferença, mas vc usando uma lista de SelectItem, vc não precisa definir o var, o itemValue e o itemLabel no seu componente no seu XHTML. Basta ter a referência para a lista.

Cola aqui o código do converter que o Flávio fez.[/quote]

Obrigado por responder Arthur.

Removi o que me passou, mas obtive o mesmo resultado.

Segue o converter:

package utils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.persistence.EmbeddedId;
import javax.persistence.Id;

/**
 * Converter para entidades JPA. Baseia-se nas anotações @Id e @EmbeddedId para
 * identificar o atributo que representa a identidade da entidade. Capaz de
 * detectar as anotações nas classes superiores.
 * 
 * @author Flávio Henrique
 * @version 1.0.3
 * @since 05/10/2010
 */
public class EntityConverter implements Converter {

	public Object getAsObject(FacesContext ctx, UIComponent component,
			String value) {
		if (value != null) {
			return component.getAttributes().get(value);
		}
		return null;
	}

	public String getAsString(FacesContext ctx, UIComponent component,
			Object obj) {
		if (obj != null && !"".equals(obj)) {
			String id;
			try {
				id = this.getId(getClazz(ctx, component), obj);
				if (id == null) {
					id = "";
				}
				id = id.trim();
				component.getAttributes().put(id,
						getClazz(ctx, component).cast(obj));
				return id;
			} catch (SecurityException e) {
				e.printStackTrace(); // seu log aqui
			} catch (IllegalArgumentException e) {
				e.printStackTrace(); // seu log aqui
			} catch (NoSuchFieldException e) {
				e.printStackTrace(); // seu log aqui
			} catch (IllegalAccessException e) {
				e.printStackTrace(); // seu log aqui
			}
		}
		return null;
	}

	/**
	 * Obtém, via expression language, a classe do objeto.
	 * 
	 * @param FacesContext
	 *            facesContext
	 * 
	 * @param UICompoment
	 *            compoment
	 * 
	 * @return Class<?>
	 */
	private Class<?> getClazz(FacesContext facesContext, UIComponent component) {
		return component.getValueExpression("value").getType(
				facesContext.getELContext());
	}

	/**
	 * Retorna a representação em String do retorno do método anotado com @Id ou @EmbeddedId
	 * do objeto.
	 * 
	 * @param Class
	 *            <?> clazz
	 * 
	 * @return String
	 */
	public String getId(Class<?> clazz, Object obj) throws SecurityException,
			NoSuchFieldException, IllegalArgumentException,
			IllegalAccessException {

		List<Class<?>> hierarquiaDeClasses = this.getHierarquiaDeClasses(clazz);

		for (Class<?> classeDaHierarquia : hierarquiaDeClasses) {
			for (Field field : classeDaHierarquia.getDeclaredFields()) {
				if ((field.getAnnotation(Id.class)) != null
						|| field.getAnnotation(EmbeddedId.class) != null) {
					Field privateField = classeDaHierarquia
							.getDeclaredField(field.getName());
					privateField.setAccessible(true);
					if (privateField.get(clazz.cast(obj)) != null) {

						return (String) field.getType()
								.cast(privateField.get(clazz.cast(obj)))
								.toString();
					}
				}
			}
		}
		return null;
	}

	/**
	 * Retorna uma lista com a hierarquia de classes, sem considerar a classe
	 * Object.class
	 * 
	 * @param Class
	 *            <?> clazz
	 * 
	 * @return List<Class<?>> clazz
	 */
	public List<Class<?>> getHierarquiaDeClasses(Class<?> clazz) {

		List<Class<?>> hierarquiaDeClasses = new ArrayList<Class<?>>();
		Class<?> classeNaHierarquia = clazz;
		while (classeNaHierarquia != Object.class) {
			hierarquiaDeClasses.add(classeNaHierarquia);
			classeNaHierarquia = classeNaHierarquia.getSuperclass();

		}
		return hierarquiaDeClasses;
	}
}

Não olhei para ver o que esse código faz… mas… você olhou se o código do converter está sendo chamado?

Sim… já debuguei pra ver se estava sendo chamado… e está sim…

Sim… já debuguei pra ver se estava sendo chamado… e está sim…[/quote]E ele está retornando um valor válido?

Sim… já debuguei pra ver se estava sendo chamado… e está sim…[/quote]E ele está retornando um valor válido?[/quote]

Sim… debugando o método getAsString funciona normalmente, e também quando seleciondo (método getAsObject), o value retorna o ID do objeto selecionado sim:

public Object getAsObject(FacesContext ctx, UIComponent component,
			String value) {
		if (value != null) {
			return component.getAttributes().get(value);
		}
		return null;
}

Mas o meu objeto Pessoa na Bean não recebe o objeto, e tenho esse retorno ai… =/

Esse seu bean Pessoa é uma entidade com anotações JPA? Mais especificamente com @Id ou @EmbeddedId? Pq esse converter para funcionar tem que ser uma entidade com essas anotações JPA.

Outra coisa: vc disse que ele retorna o ID corretamente, mas ele executa essa linha daqui sem erro?

return component.getAttributes().get(value);

[quote=Arthur F. Ferreira]Outra coisa: vc disse que ele retorna o ID corretamente, mas ele executa essa linha daqui sem erro?

return component.getAttributes().get(value); [/quote]

O erro dá justamente neste return…

A entidade em questão foi um teste que fiz bem simples, pra excluir a possibilidade de ser alguma outra coisa “externa”, pra isto, criei essa entidade simples pra ver o que estava acontecendo:

[code]

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Index;

@Entity
public class Pessoa {

@Id
@GeneratedValue(generator = "gen_pessoa")
@GenericGenerator(name = "gen_pessoa", strategy = "increment")
@Column(name = "id", nullable = false)
private Integer id;

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

@Override
public int hashCode() {
	final int prime = 31;
	int result = 1;
	result = prime * result + ((id == null) ? 0 : id.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;
	Pessoa other = (Pessoa) obj;
	if (id == null) {
		if (other.id != null)
			return false;
	} else if (!id.equals(other.id))
		return false;
	return true;

/// Getters e Setters

}[/code]

Qual é o valor exato que vem no value?

E quando vc seleciona esse código (sem o return) e usa o CTRL+SHIFT+I, qual é a mensagem de erro detalhada que aparece para vc?

[quote=Arthur F. Ferreira]Qual é o valor exato que vem no value?

E quando vc seleciona esse código (sem o return) e usa o CTRL+SHIFT+I, qual é a mensagem de erro detalhada que aparece para vc?[/quote]

O Value vem o id da pessoa corretamente, já me certifiquei disso.

Não conhecia o CTRL+SHIFT+I… Já me ensinou mais uma =D

Resultado:

ID = NULL
NOME = NULL

É…já dá para ver que tem algo de errado e provavelmente é a maneira com que o EntityConverter faz para recuperar um registro.

O converter que eu uso é diferente. Eu sigo o exemplo do Rafael Ponte:

http://www.rponte.com.br/2008/07/26/entity-converters-pra-da-e-vender/

Hoje mesmo no trabalho um colega estava tendo problemas com o converter que usava. Ele usou a implementação do SimpleIndexConverter que é a mais simples e, no seu caso, acho que será a melhor.

[quote=Arthur F. Ferreira]É…já dá para ver que tem algo de errado e provavelmente é a maneira com que o EntityConverter faz para recuperar um registro.

O converter que eu uso é diferente. Eu sigo o exemplo do Rafael Ponte:

http://www.rponte.com.br/2008/07/26/entity-converters-pra-da-e-vender/

Hoje mesmo no trabalho um colega estava tendo problemas com o converter que usava. Ele usou a implementação do SimpleIndexConverter que é a mais simples e, no seu caso, acho que será a melhor.[/quote]

Maravilha Arthur! Utilizei o SimpleIndexConverter da mesma maneira que estava usando o EntityConverter e funcionou de primeira!

Muito obrigado pela prestatividade!

Um abraço…