Entity Converters pra dar e vender

Uma coisa que sempre aconselho aos desenvolvedores é que tentem sempre que possível trabalhar em JSF diretamente com os objetos como se estivessem em um ambiente stateful, pois um dos objetivos da tecnologia é tentar abstrair a natureza stateless do HTTP. Não que seja algo tão simples de se fazer algumas vezes, mas não é tão complexo ao ponto de abrir mão desta abstração.

Pensando no que eu disse acima, um dos problemas comuns e chatinhos quando se trabalha com SelectOneMenu (ou SelectManyMenu) e entidades em JSF ocorre quando queremos que o value do nosso SelectItem seja a própria entidade, e não o “id” da mesma. Bem, o que estou querendo dizer é exatamente isso:

public List<SelectItem> getEmpresas() {
	List<SelectItem> items = new ArrayList<SelectItem>();
	for (Empresa e : this.empresas) {
		// observem que o value do meu SelectItem é a própria entidade
		items.add(new SelectItem(e, e.getNome()));
	}
	return items;
}

Isso acaba se tornando trabalhoso pois somos obrigados a implementar um converter para cada entidade, o que particularmente eu não gosto. Levando em consideração que não queremos criar um converter para cada entidade, quais outras soluções temos?

Cenário

Antes de explicar cada solução, vou demonstrar um cenário qualquer para facilitar o entendimento, ou seja, vou demonstrar os artefatos necessários, eles seguem abaixo:

1) Nosso managed bean

public class EmpresaBean {

	private Empresa selectedEmpresa;
	private List<Empresa> empresas = new ArrayList<Empresa>();

	public EmpresaBean() {
		empresas.add(new Empresa(7, "Triadworks"));
		empresas.add(new Empresa(88, "Ivia"));
		empresas.add(new Empresa(921, "Thoughtworks"));
		empresas.add(new Empresa(15, "Caelum"));
		empresas.add(new Empresa(2, "ImproveIT"));
	}

	public List<Empresa> getEmpresas() {
		return empresas;
	}
	public Empresa getSelectedEmpresa() {
		return selectedEmpresa;
	}
	public void setSelectedEmpresa(Empresa selectedEmpresa) {
		this.selectedEmpresa = selectedEmpresa;
		System.out.println("Empresa selecionada: " + selectedEmpresa.getNome());
	}
}

2) Nossa entidade (já implementando a interface BaseEntity para a solução do SimpleEntityConverter)

public class Empresa implements BaseEntity, Serializable {

	private static final long serialVersionUID = 1L;

	private Integer codigo;
	private String nome;

	public Empresa(Integer codigo, String nome) {
		this.codigo = codigo;
		this.nome = nome;
	}

	public Long getId() {
		return new Long(codigo);
	}

	// Métodos getters e setters
	// Não esquecer os métodos equals e hashCode
}

Por favor, não esqueçam de implementar os métodos equals e hashCode, evitemos cair naquele velho probleminha, ok?

3) Nosso formulário

<h:form id="form">
	<h:panelGrid columns="2" border="1">
		<h:outputLabel value="Empresa" for="empresa"/>
		<h:column>
			<h:selectOneMenu id="empresa"
				value="#{empresaBean.selectedEmpresa}"
				converter="simpleEntityConverter" required="true"
				requiredMessage="Valor é obrigatório">
				<f:selectItem itemValue="" itemLabel="Selecione uma empresa"/>
				<t:selectItems value="#{empresaBean.empresas}" var="o" itemLabel="#{o.nome}" itemValue="#{o}"/>
			</h:selectOneMenu>
			<br/>
			<h:message for="empresa" errorStyle="color:darkred;font-size:11px;"></h:message>
		</h:column>
		<f:facet name="footer">
			<h:commandButton value="Submit"/>
		</f:facet>
	</h:panelGrid>
</h:form>

Reparem que nosso componente h:selectOneMenu possui um converter declarado, ou seja, será necessário alterar o converter de acordo com a solução escolhida.

Nada de complexo, certo? É um básico cenário comum.
Então vamos as possíveis soluções 🙂

EntityConverter

O componente/feature EntityConverter foi desenvolvido pelo Rogério Araújo, que é um dos coordenadores do JavaServer Faces International Group. O componente em si foi pensado inicialmente para o cenário em que é necessário carregar a entidade de um banco de dados, mas isso não impede que a entidade seja obtida de qualquer outro recurso externo, como um web service ou EJB, por exemplo.

O componente funciona perfeitamente bem e é muito simples de configurar, porém ainda assim eu o acho interessante somente em alguns casos específicos, como no caso em que a entidade precisa ser carregada do banco de dados com todos seus atributos e/ou relacionamentos ao submeter o formulário, evitando-se assim onerar o servidor com grandes entidades em memória.

Alguns desenvolvedores reclamam porque o componente -idealmente- efetua uma requisição ao bando de dados para obter a entidade, mas venhamos e convenhamos, isso não deveria ser uma preocupação, principalmente para quem vem de algum framework “action-like” como o Struts. Além do mais, se você possui algum recurso de cache na camada de persistência -como o fornecido pelo Hibernate– não deveria se preocupar tanto com isso.

Eu não pretendo explicar como configurar o componente, pois na wiki do Myfaces você tem tudo que precisa saber, além do mais o criador do componente é coordenador da Javasf e é brasileiro 🙂

SimpleEntityConverter

Esta solução foi baseada no caso mais comum de um converter para cada entidade, a única diferença é que tentei torna-la “genérica” para qualquer entidade, evitando-se escrever um converter para cada entidade (é esta nossa intenção, certo?).

Sendo, segue abaixo o código do nosso converter:

public class SimpleEntityConverter implements Converter {

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

	public String getAsString(FacesContext ctx, UIComponent component, Object value) {

		if (value != null
				&& !"".equals(value)) {

			BaseEntity entity = (BaseEntity) value;

			// adiciona item como atributo do componente
			this.addAttribute(component, entity);

			Long codigo = entity.getId();
			if (codigo != null) {
				return String.valueOf(codigo);
			}
		}

		return (String) value;
	}

	protected void addAttribute(UIComponent component, BaseEntity o) {
		String key = o.getId().toString(); // codigo da empresa como chave neste caso
		this.getAttributesFrom(component).put(key, o);
	}

	protected Map<String, Object> getAttributesFrom(UIComponent component) {
		return component.getAttributes();
	}

}

Como podem ver, o que basicamente ocorre é que armazenamos as entidades como atributos do nosso componente no método getAsString(), e recuperamos a entidade correta através da chave (neste caso o Id) associada a ela, no método getAsObject(). Mas quem é este BaseEntity?

public interface BaseEntity {

	public Long getId();

}

BaseEntity é uma interface com um método em comum entre as entidades, ou seja, nossas entidades precisarão implementar esta interface para que nosso converter funcione de acordo.

A solução é funcional, porém há algumas ressalvas que gostaria de explicitar. 1) Eu particularmente não gosto da idéia de “sujar” minhas entidades estendendo alguma interface ou classe para poupar código ou resolver problemas não-funcionais, mas isso é uma opinião minha. 2) Outro problema que vejo é que este converter acaba consumindo um pouco mais de memória no servidor por você está alterando o estado do componente, principalmente se sua lista de itens for muito grande, mas não é nada que você deva se preocupar inicialmente, ou seja, somente esquente a cabeça com isso se for realmente necessário.

SimpleIndexConverter

Esta solução eu tomei emprestada dos componentes do Myfaces Trinidad, que por sinal é excelente. A diferença é que eu a simplifiquei um pouco (o código para ser mais exato), logo ela não está tão robusta quanto a original (que se preocupa com casos mais específicos), mas funciona muito bem na maioria dos casos.

Sua idéia principal é utilizar o index da lista de items como chave para cada entidade, assim o que submetemos no formulário é o index da lista, e não um valor (Id?) da entidade como de costume.

O código do converter ficou grandinho, mas é possível enxuga-lo movendo alguns métodos para alguma classe utils. Segue abaixo o código do nosso SimpleIndexConverter:

public class SimpleIndexConverter implements Converter {

	private int index = -1;

	/* (non-Javadoc)
	 * @see javax.faces.convert.Converter#getAsObject(javax.faces.context.FacesContext, javax.faces.component.UIComponent, java.lang.String)
	 */
	public Object getAsObject(FacesContext ctx, UIComponent component, String value) {

		SelectItem selectedItem = this.getSelectedItemByIndex(component, Integer.parseInt(value));
		if (selectedItem != null)
			return selectedItem.getValue();

		return null;
	}

	/* (non-Javadoc)
	 * @see javax.faces.convert.Converter#getAsString(javax.faces.context.FacesContext, javax.faces.component.UIComponent, java.lang.Object)
	 */
	public String getAsString(FacesContext ctx, UIComponent component, Object value) {
		index++;
		return String.valueOf(index);
	}

	/**
	 * Obtem o SelecItem de acordo com a opção selecionada pelo usuário
	 */
	protected SelectItem getSelectedItemByIndex(UIComponent component, int index) {

		List<SelectItem> items = this.getSelectItems(component);
		int size = items.size();

		if (index > -1
				&& size > index) {
			return items.get(index);
		}

		return null;
	}

	protected List<SelectItem> getSelectItems(UIComponent component) {

		List<SelectItem> items = new ArrayList<SelectItem>();

		int childCount = component.getChildCount();
	    if (childCount == 0)
	      return items;

	    List<UIComponent> children = component.getChildren();
		for (UIComponent child : children) {
			if (child instanceof UISelectItem) {
				this.addSelectItem((UISelectItem) child, items);
			} else if (child instanceof UISelectItems) {
				this.addSelectItems((UISelectItems) child, items);
			}
		}

		return items;
	}

	protected void addSelectItem(UISelectItem uiItem, List<SelectItem> items) {

		boolean isRendered = uiItem.isRendered();
		if (!isRendered) {
			items.add(null);
			return;
		}

		Object value = uiItem.getValue();
		SelectItem item;

		if (value instanceof SelectItem) {
			item = (SelectItem) value;
		} else {
			Object itemValue = uiItem.getItemValue();
			String itemLabel = uiItem.getItemLabel();
			// JSF throws a null pointer exception for null values and labels,
			// which is a serious problem at design-time.
			item = new SelectItem(itemValue == null ? "" : itemValue,
					itemLabel == null ? "" : itemLabel, uiItem
							.getItemDescription(), uiItem.isItemDisabled());
		}

		items.add(item);
	}

	@SuppressWarnings("unchecked")
	protected void addSelectItems(UISelectItems uiItems, List<SelectItem> items) {

		boolean isRendered = uiItems.isRendered();
		if (!isRendered) {
			items.add(null);
			return;
		}

		Object value = uiItems.getValue();
		if (value instanceof SelectItem) {
			items.add((SelectItem) value);
		} else if (value instanceof Object[]) {
			Object[] array = (Object[]) value;
			for (int i = 0; i < array.length; i++) {
				// TODO test - this section is untested
				if (array[i] instanceof SelectItemGroup) {
					resolveAndAddItems((SelectItemGroup) array[i], items);
				} else {
					items.add((SelectItem) array[i]);
				}
			}
		} else if (value instanceof Collection) {
			Iterator<SelectItem> iter = ((Collection<SelectItem>) value)
					.iterator();
			SelectItem item;
			while (iter.hasNext()) {
				item = iter.next();
				if (item instanceof SelectItemGroup) {
					resolveAndAddItems((SelectItemGroup) item, items);
				} else {
					items.add(item);
				}
			}
		} else if (value instanceof Map) {
			for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) value).entrySet()) {
				Object label = entry.getKey();
				SelectItem item = new SelectItem(entry.getValue(),
						label == null ? (String) null : label.toString());
				// TODO test - this section is untested
				if (item instanceof SelectItemGroup) {
					resolveAndAddItems((SelectItemGroup) item, items);
				} else {
					items.add(item);
				}
			}
		}
	}

	protected void resolveAndAddItems(SelectItemGroup group, List<SelectItem> items) {
		for (SelectItem item : group.getSelectItems()) {
			if (item instanceof SelectItemGroup) {
				resolveAndAddItems((SelectItemGroup) item, items);
			} else {
				items.add(item);
			}
		}
	}

}

Não há muito a se explicar sobre o converter acima, a “mágica” toda está nos métodos auxiliares (que por sinal maior parte do código foi retirado do Trinidad, porém enxugado para nossas necessidades).

Este converter diferentemente do SimpleEntityConverter não altera o estado do componente, evitando assim a utilização de mais memória, ele busca as entidades -que já existem- dentro do componente (SelectOneMenu?) como estado do mesmo.

Assim como os outros converters, este também possui algumas ressalvas. O que podemos citar é 1) Por ele se utilizar do index da lista de items talvez não seja possível trabalhar com o componente no lado cliente (javascript) se você depender dos valores (Id?) de cada item.

Concluindo

Dizer que não é possível simplificar a vida com JSF é mentir, a tecnologia te fornece recursos para abstrair a natureza stateless do HTTP, e é interessante que se aproveite destes recursos.

As soluções explanadas acima não são únicas, existem outras com toda certeza, eu tentei demonstrar algumas delas quando não se tem algum framework (Seam?) ou conjunto de componentes (Trinidad?) para nos auxiliar, ou seja, quando contamos apenas com a implementação do JSF, o que -acredito eu- na maioria dos casos é o que ocorre.

É possível estende-las e até melhora-las, você é livre para isso, e dependendo da tua necessidade provavelmente será o melhor caminho, só não deixe de contribuir com o código para a comunidade.

Enfim, todas as soluções são plausíveis e funcionam para a maioria dos cenários, porém em determinados cenários cada uma se adequa melhor, resta a você analisar e ver qual se encaixa nas tuas necessidades. Além do mais, elas não são mutuamente excludentes.

118 Replies to “Entity Converters pra dar e vender”

  1. Rapaz, num sei se vou falar besteira, mais eu faço diferente.

    Como no meu MB eu tenho o grafo que necessito para submeter ao framework de persistência,só preciso apontar o selectonemenu ou qq coisa pra o cara, pois sua instancia já existe e não faço nenhum converter…

    Pode ser mais uma forma

    Nois

    Pompommmm

  2. Cara, esse seu post vai me ajudar muito. Já penei bastante na busca de uma boa solução de entityConverter pra usar com o t:selectOneMenu. Não li todo agora,mas pelo visto está bem completo. Amanha vou ler bem concentrado hehehehehe.

    []s e Parabens

  3. muito bom ponte essa solucao..!!

    ..la na empresa como nao utilizamos JSF puro temos auxilio do Seam que facilita N coisas..

    excelente post, parabens! []’s

  4. Ficou muito bem legal as ideias postadas. Gostei bastante e abriu um leque de opções para o pessoal começar a pensar no que podem fazer.

    Muito bom.

  5. Rafael, esse post realmente e de grande utiliadade e com certeza vai ajudar bastante nos futuros projetos. Porém gostaria de tirar uma dúvida, porque o SimpleIndexConverter não funciona quando o meu “selectManyCheckbox” onde estão meus “selectItems”, por exemplo, e criado linha a linha atraves da iteração de uma lista em uma “dataTable”?
    Como exemplo, podemos imaginar uma lista de usuários e seus grupos, sendo que em cada linha da tabela será mostrado o usuário (#{meuMBean.usuarios}) e a lista de grupos (checkboxes) aos quais ele pode pertencer.

    Mais claramente:

    <h:selectManyCheckbox value="#{meuMBean.usrGrp[usr]}" <!-- HasMap<Usuario, List> -->
    layout="pageDirection"
    converter="simpleEntityConverter">

    Caso esse seja uma problema não previsto ou testado, como poderia ser corrigido???

    Parabéns pelo post, apesar desse detalhe continua sendo de grande valia.

    Até mais.

  6. Olá Manuel,

    Obrigado, essa é com certeza a intenção deste post e da maioria dos meus posts.

    Sinceramente, não tive a oportunidade de trabalhar com o componente h:selectManyCheckbox ainda, logo teria que ver como ele funciona (mesmo acreditando que não seja diferente de um h:selectOneMenu), talvez eu devesse fazer alguns testes isolados para ver como o mesmo trabalha.

    Você poderia explicar-me qual problema extamente está acontecendo?

    Abraços!

  7. Olá Manuel,

    Eu acabei de fazer os testes com o SimpleIndexConverter e o componente h:selectManyCheckbox e obtive sucesso, a conversão funcionou sem problemas.

    Você poderia me explicar quais problemas você está tendo com o converter e o componente?

    Abraços.

  8. Olá Rafael,

    acho que deu um problema na hora de colar o código aqui, mas vamos tentar novamente.

    <h:dataTable value="#{meuBean.usuarios}" var="usr">
    <h:selectManyCheckbox value="#{meuMBean.usrGrp[usr]}" <!– HasMap<Usuario, List> –>
    layout="pageDirection"
    converter="simpleEntityConverter">
    </h:dataTable>

    No meu caso específicamente, eu carrego a dataTable através de uma chamada ajax ao servidor, dependendo de uma determinada ação do usuário.

    O que eu pude perceber foi que se eu selecionar opções apenas do primeiro selectManyCheckbox, tudo funciona bem, o erro ocorre se eu seleciona alguma opção dos outros selectManyCheckBox.

    É isso aí, tente simular essa situação aí e veja se ocorre algum erro. Caso realmente exista esse problema e vc encontre uma forma de resolve-lo, por favor nos informe.

    Abraço.

  9. Olá rafael! Muito bom o blog. Inclusive estou tendo problemas com o componente rich:fileUpload , preciso capturar o evento onuploadcomplete para recarregar a tabela de arquivos que já foram enviados. Porém nenhum envento da tag está realmente chamado uma simples funçao javascript de alerta: “onclick=alert(‘teste’);”. Você possui algum exemplo pronto de upload, ou pode ajudar neste caso?

  10. Bom dia Rafael!
    Parabéns pelo post. É o seguinte eu estou começando a trabalhar com Java e principalmente JSF. To precisando implementar um selectOneMenu na minha aplicação, que foi gerada pelo JBoss Seam, só que não consigo. Será que você poderia me ajudar?
    Se você puder me mande um e-mail que eu te envio o meu projeto para você dar uma olhada.

    Muito Obrigado.

  11. Olá Rafael,

    Conforme o post de nosso amigo Manuel

    “acho que deu um problema na hora de colar o código aqui, mas vamos tentar novamente.

    <h:selectManyCheckbox value=”#{meuMBean.usrGrp[usr]}” <!– HasMap –>
    layout=”pageDirection”
    converter=”simpleEntityConverter”>

    No meu caso específicamente, eu carrego a dataTable através de uma chamada ajax ao servidor, dependendo de uma determinada ação do usuário.

    O que eu pude perceber foi que se eu selecionar opções apenas do primeiro selectManyCheckbox, tudo funciona bem, o erro ocorre se eu seleciona alguma opção dos outros selectManyCheckBox.

    É isso aí, tente simular essa situação aí e veja se ocorre algum erro. Caso realmente exista esse problema e vc encontre uma forma de resolve-lo, por favor nos informe.”

    Estou com o mesmo problema você sabe como poderia corrigir isto?

  12. Olá Rafael Ponte e Rafael,

    bem no caso mensionado acima, o problema só ocorre quando eu utilizo o SimpleIndexConvereter, quando utilizado o SimpleEntityConverter tudo funciona bem.

    Rafael,

    no caso da utilização do ajax, verifique se vc está utilizando algum recurso para extender o escopo do ManagedBean, caso esse esteja com escopo de request. Esse é um problema bastante recorrente que muitos já passaram e/ou devem passar ainda, assim como eu.
    Se vc estiver utilizando o RichFaces procure sobre a4j:keepAlive, se caso estiver utilizando o Tomahawk procure sobre t:saveState.

    Espero ter ajudado.

    OBS.: Em ambos posts que eu enviei, no código de exemplo está sendo utilizado SimpleEntityConverter, nesse caso funciona.

    Abraços.

  13. Cara deu certo aqui sim o esquema de usar o converter o problema esta sendo em usar o a4j:keepAlive pois estou usando assim:

    Queria que redirecionasse o valor do bean para meu campo intputHidden mas não dando certo esta perdendo o valor se tiver alguma dica…Abraços

  14. Não consegui postar o fonte me passa seu e-mail Manuel que te mando o codigo…Abralos

  15. Olá, quero registrar meu agradecimento pela deste post. Obrigado

  16. O Rafael deu dicas para implementação de Entity Converters usando somente JSF, mas só para constar: o Seam possui uma tag com a com vc nem precisa implementar esses converters. Minha implementação de um menu de empresas ficou assim…

    public List getOpcoesEmpresas() {

    List opcoes = new ArrayList();

    for (EmpresaUsuario empresaUsuario : usuario.getEmpresas()) {
    opcoes.add(empresaUsuario.getEmpresa());
    }

    return opcoes;
    }

    Seamples, não?… 🙂

  17. Tentei postar o código do XHML, mas não foi…

  18. Troquei por $:

    $h:selectOneMenu value=”#{usuario.empresaLogin}”$
    $s:selectItems var=”empresa” value=”#{menu.opcoesEmpresas}” label=”#{empresa.nome}”/$
    $s:convertEntity/$
    $h:selectOneMenu/$
    $a4j:commandButton id=”ok” action=”#{menu.emp}” value=”OK”/$

  19. Cara gostei muito do poste mas estou com um probleminha não consigo configurar o myface no web.xml. Como faço?

  20. Rafael,

    Como que você conseguiu colocar na sua classe essa linha:

    Você fez a importação de qual biblioteca?

  21. Aqui vai o código:

    $t:selectItems value=”#{empresaBean.empresas}” var=”o” itemLabel=”#{o.nome}” itemValue=”#{o}”/$

  22. Rafael,

    funcionou muito bem. Fiz o exemplo exatamente como você escreveu e funcionou. Usei o bean em escopo de REQUEST e pra manter o estado usei .

    Abraços…

  23. Acho que o blog não deixa postar tags, vou trocar por cifrão: $a4j:keepAlive beanName=”empresaBean” /$

  24. Rafael, veja se pode me ajudar.
    Fiz um exemplo semelhante ao seu, porém, surge o seguinte erro:

    javax.servlet.ServletException: Property ‘nm_pais’ not found on type javax.faces.model.SelectItem
    javax.faces.webapp.FacesServlet.service(FacesServlet.java:277)
    org.apache.myfaces.webapp.filter.ExtensionsFilter.doFilter(ExtensionsFilter.java:341)

    Este erro ocorre pq eu coloco no t:selectItems o itemLabel=”#{j.nm_pais}”:

    Obrigado!
    Abraço.

  25. Olá Rafael, show de bola teus posts… muito bons mesmo. Mas aqui numa aplicação JSF 1.2 + Facelets o conversor só funcionou ao colocar o MB como session Scope… é isso mesmo ?

  26. Carlos,

    Não, o converter não tem nada a ver com o escopo do managed bean. Provavelmente teu problema está relacionado ao escopo da lista de valores possíveis que é retornada para teu componente (f:selectItems).

    Dá uma verificada nisso, acho que os valores (lista) estão se perdendo entre as requisições.

    Abraços e boa sorte.

  27. Rafael, parabéns pelo post.

    Utilizei a SimpleIndexConverter mas não funcionou no MyFaces 1.2.7. O componente SelectOneMenu primeiramente chama o método getAsString() para converter o valor atual do componente, depois as chamadas são feitas para cada selectItem. Daí o primeiro item era gerado com valor 1, sendo que deveria ser gerado com valor 0, que é o índice do primeiro item.
    A solução que encontrei foi verificar se o value é null (que é o valor atual do component selectOneMenu). Ficou assim:

    if (value == null)
    return “”;
    index++;
    return String.valueOf(index);

    Desta forma funcionou. Mas eu ainda não testei com SelectItemGroup.

    Muito obrigado pelo post. Me ajudou muito.
    []’s

  28. Tentei implementar este segundo converter (“SimpleIndexConverter”) mas não rolou..ocorre o seguinte erro: Conversion Error setting value ‘br.gov.ce.saude.spiderv5.model.entity.RacacorEntity[id=2]’ for ‘null Converter’.

  29. Olá Rafael,

    Estou com um probleminha com o Converter. Deixa eu te explicar minha situacao. Tenho um suggestionBox (City) + 2 combobox (state e Country) aninhados. Vou digitando a cidade e quando encontro, automaticamente state e country são carregados. Até ai tudo bem, o problema rola quando clico para submeter o campo, ao escolher cidade como Campinas:
    São Paulo
    Brasil
    Será carregado assim o combobox. Ao submeter ele pega o getAsObject e executa e retorna o objeto state só que não no value this.state do meu managedBean.
    Ou seja o objeto retornado pelo getAsObject está por exemplo na variável obj e o this.state estão com mesmo valor = São Paulo. Quando mudo para o estado de New York digitando a cidade Tarrytown, o obj continua São Paulo , o state é atualizado para New York mas por alguma razão ao pegar a lista de estados dos estados unidos teoricamente ele devia ver o q está gravado em New York deveria deixa selecionado na lista o estado New York dos estados americanos. Mas por alguma razão ele está pegando do OBJ e não do THIS.STATE. Alguma ajuda?

  30. Fiz uma alteração no SimpleEntityConverter que ficou bem interesante, para localizar o atributo ID, eu optei por localizar qual atributo está anotado como @ID no meu pojo usando Reflection, desta forma meus POJOS não precisam implementar nenhuma interface =) !

    Excelente artigo, Abraço !!

  31. Oi Mateus,

    Eu havia pensado nesta solução também quando escrevi este post, alias, tenho quase certeza que o JBoss Seam com seu s:entityConvert se utiliza desse conceito. Provavelmente ele mescla a 1a solução do post, EntityConverter, já que é o Seam quem gerencia o converter.

    Sua solução, e consequentemente do JBoss Seam, está atrelada ao JPA, o que eu não vejo problema algum e acho até mais “clean” do que minhas entidades implementarem uma interface. O SimpleIndexConverter é uma solução mais generica, independe do framework de persitência, mas também tem suas desvantagens como citado no post.

    Vi que você tem um blog, e se possível, seria interessante você blogar sobre seu entity-converter. Quando postar, não deixe de me avisar.

    Enfim, parabéns pela solução.

  32. Nota:

    O SimpleIndexConverter funcionou de maneira incorreta quando utilizado com o Myfaces 1.2.8. Por algum motivo em especial o método getAsString() do converter é chamado duas vezes ao renderizar a página, fazendo com que o indice inicial da lista de itens comece de 1 e não 0 (zero). Trazendo então problemas na indexação dos itens do componente ao submeter o formulário.

    Enfim, a solução mais simples, e sempre aconselhada, para Jsf1.2, é utilizar a implementação de referência, Mojarra. Pois com toda certeza ela é a mais estável entres as opções atualmente.

    #fikadica

  33. Rafael não achei a interface BaseEntity ela esta em qual jar?

  34. Olá Rafael,

    Preciso de umas dicas suas a respeito de um formulário que corresponde ao registro do usuário no meu sistema. Neste formulário, tenho informações do usuário bem como o Estado e Cidade onde ele está localizado.

    Eu havia criado um managedBean para o “Usuário”, chamado UsuarioBean e outro com informações de “Estado” e “Cidade” chamado LocalizacaoBean. Por que inseri na mesma classe/managedBean? Pois eu não tinha idéia de como recuperar a informação do estado para listar as cidades. O método que criei (“changeCidades “) requer a informação de Estado para realizar o filtro das cidades.

    Para ligar os mbs Usuario e Localizacao, utilizei o recurso de “dependency injection”.

    Testei a implementação (sei que dessa maneira não está legal) e a lista de estado e cidade estavam filtrando ok. Porém, percebi que ao clicar no botão salvar, ocorre o erro “j_idt60:cidades: Erro de validação: o valor não é válido” que é justamente o que consta aqui neste seu post . Terei que criar os converter’s e também sobreescrever os métodos equals() e hashCode() . Além disso, alterar o que citei sobre os managedBean’s.

    Aí seguem as minhas dúvidas:

    1) Não seria correto separar tudo (os managedBeans) ? Tenho os POJOs Usuario, Estado e Cidade já relacionados. Agora e os managedBeans ? Pergunto pois separando, como iria interligar um ao outro para obter as informações (ao salvar os dados) ? Comecei a separar com o recurso de DI, conforme citado mas imagino que não seja a solução correta.

    2) Pelo que vi, os converters são usados para conversão, como o próprio nome já diz para uma lista de valores que são os famosos combobox (porém certamente utilizados para outros componentes). No meu caso, o EstadoBean teria o converter para ele assim como para CidadeBean. Agora, para Usuário, eu não preciso ter um converter correto?

    Obrigado,
    Rodrigo

  35. Caro Rafael,

    Desculpe pela “redação” postada ai logo acima, hehe. Li com mais atenção sobre converters e analisei alguns exemplos. Percebi que estou completamente equivocado. Converter serve para os POJOS não para os MBs certo? Fechou então! Vou fazer os converters para Estado e Cidade e ligar os 2 managedBeans como havia citado. Um é o UsuarioBean e o outro é o LocalizacaoBean que contém info do Estado e Cidade. Ai já com o override de equals e hashcode nas classes, acho que é por aí certo ?

    Abraço,
    Rodrigo

  36. Olá Rafael, olha só como eu fiz o ManagedBean para o caso de combos aninhadas (Estado/Cidade) porém eu não utilizei converters. Fiz baseado no que foi postado pelo Sergio Fantin no link http://serjaum.wordpress.com/2009/08/28/jsf-tutorial-combos-aninhados-estadoscidades/

    @ManagedBean(name = "regiaoMB")
    @ViewScoped
    public class RegiaoBean implements Serializable {
    
    	private static final long serialVersionUID = -5761826935504914211L;
    
    	private Estado estado = new Estado();
    	private Cidade cidade = new Cidade();
    	private List cidades = new ArrayList();
    	
    	public List getEstados(){
    		List itens = null;
    		RetornoEstado tResult = EstadoController.pesquisarEstados();
    		if (tResult.isOk()) {
    			Iterator iterator = tResult.getListaEstados().iterator();
    			itens = new ArrayList(tResult.getListaEstados().size());
    			while (iterator.hasNext()) {
    				Estado estado = (Estado) iterator.next();
    				itens.add(new SelectItem(estado.getIdEstado(), estado.getEstado()));
    			}
    		}
    		return itens;
    	}
    	
    	public void actionCarregaCidades(){
    		System.out.println("id do estado selecionado &gt;&gt;&gt; " + this.estado.getIdEstado());
    		this.cidades = this.getCidadesByEstado();
    	}
    	
    	public List getCidadesByEstado(){  
    		List itens = null;
    		RetornoCidade tResult = CidadeController.pesquisarCidades(this.estado);
    		if (tResult.isOk()) {
    			Iterator iterator = tResult.getListaCidades().iterator();
    			itens = new ArrayList(tResult.getListaCidades().size());
    			while (iterator.hasNext()) {
    				Cidade cidade = (Cidade) iterator.next();
    				itens.add(new SelectItem(cidade.getIdCidade(), cidade.getCidade()));
    			}
    		}
    		return itens;
    	}
    
    	public Estado getEstado() {
    		return estado;
    	}
    	public void setEstado(Estado estado) {
    		this.estado = estado;
    	}
    	public Cidade getCidade() {
    		return cidade;
    	}
    	public void setCidade(Cidade cidade) {
    		this.cidade = cidade;
    	}
    	public List getCidades() {
    		return cidades;
    	}
    	public void setCidades(List cidades) {
    		this.cidades = cidades;
    	}	
    }
    

    O que entendi foi que neste caso eu só terei o id (tanto da cidade quanto do estado) como retorno. Se precisasse de mais informações (um objeto com 10 atributos) eu teria que fazer o uso do converter para então recuperar o objeto pelo nome/id correto?

    Abraço,
    Rodrigo

  37. Oi Rodrigo,

    Na verdade converters servem para qualquer tipo de objeto complexo, não necessariamente precisa ser um pojo, poderia ser um List ou Map, por exemplo.

    Existem vários caros onde usar converters é interessante, um deles, por exemplo, é quando você estiver trabalhando diretamente com objetos de domínio ricos (que possuem lógica de negócio) e quer recebe-los (já populado e preparado) diretamente dentro do managed bean.

    No mais, não entendi bem onde se encontra tua real dúvida. Se puder explicar de novo eu ficaria grato.

    Um abraço.

  38. Olá Rafael,

    Valeu pela resposta amigão! A minha real dúvida é a seguinte: para os campos de seleção Estado e Cidade eu utilizei o managedBean acima citado RegiaoBean. Este managedBean eu uso então para as 2 combobox. Um dos exemplos que vi na internet; por exemplo, o managedBean UserBean onde dentro dele há o atributo User e outro atributo List users. Até aí tudo bem pois esse UserBean eu poderia fazer um converter para ser utilizado numa combobox contendo lista de usuários. Mas no meu caso onde o managedBean é composto (Estado e cidade juntos) ? Como implementar a classe converter para este bean ? Minhas classes Estado e Cidade estão da forma a seguir (fiz simplificado aqui sem os modificadores e get/setters):

    
    public class Estado {
        String idEstado;
        String nomeEstado;
    }
    
    public class Cidade {
        String idCidade;
        String nomeCidade;
        Estado estado;
    }
    

    Na realidade, para este caso eu consegui que funcione sem converter e utilizando o escopo view. Mas fiquei curioso em saber como faria se utilizasse converters (assim eu teria o retorno completo dos objetos certo?).

    Obrigado!
    Abraço,
    Rodrigo

  39. Olá Rodrigo,

    Você precisa implementar os converters para ambos, Cidade e Estado, declara-los no faces-config.xml (para jsf 1.2), configurara-los nos componentes da página, e escrever getters e setters para os objetos no teu managed bean. Basicamente isso!

    Se sua aplicação está funcionando bem sem converters *neste caso* então talvez você não precise deles. O que indico, considerando suas dúvidas, é ler sobre como converters funcionam.

    Escreva alguns converters para cada entidade/pojo que precisar nas páginas para entender o conceito e só então procure implementar ou utilizar algum mais genérico, como os deste post.

  40. Rodrigo,

    Só para deixar claro, você não implementa converters para seus managed bean (quem sabe em casos estritamente raros e complexos, não sei), você os implementa para traduzir “strings” (parâmetros http vindos da página/browser) para objetos de negócio ou simplesmente objetos complexos no seu managed bean, ou seja, no lado servidor.

  41. Opa, muito obrigado pelas respostas Rafael ! Vou estudar melhor e fazer exemplos!

    Abraço!
    Rodrigo.

  42. Opa eae blz??

    Cara só uma dúvida!

    na página vc chama converter= “simpleEntityConverter” esse cara tem que ser configurado no faces-config.xml??

    ele está dando um erro falando que não encontrou..

    =[

  43. Olá Rafael, primeiramente parabens cara, já vi essa tua cara aí em alguns fóruns, mas vô adicionar o blog aqui nos favoritos. Sei que devo estár só uns 4 meses após teu ultimo feedback neste post mas vamos lá…
    Bom, pra começar, entendi que a tua primeira solução é interessante, porém não conserva o estado do objeto da view, pois ele o busca no banco novamente, ou seja, num caso esporádico onde o selectItem pode ser alterado(na view) essa solução não se aplica certo? além de mais recurso de memória…
    Por outro lado a segunda depende também da view pois está terá certamente um index, e na view, alguém de alguma forma pode alterar este index… fazendo com que o converter não consiga comparar os objetos(acho que é isso)…
    Agora é o seguinte, não tem nada no JSF 2.0 para sanar isso? e mais, eu preciso mesmo de bibliotecas do myfaces para implementar o “SimpleIndexConverter” ou pode ser as jsf-api, jsf-impl, etc… é que não vi teus imports…. e estou começando a entender muitas coisas agora, ainda não tenho nenhuma experiência.

    Obrigado.

  44. Oi Joilson,

    Qualquer uma das 3 soluções acima se aplica a qualquer caso, só muda a estratégia, mas no final o resultado é o mesmo.

    Nenhuma das estratégias acima está presa a qualquer implementação ou conjunto de componentes JSF, ou seja, você pode utilizar qualquer uma delas com Myfaces ou Mojarra (e Primefaces ou Icefaces etc) e até mesmo com JSF2.

  45. Ah certinho Rafael, eu testei ontem(ou hoje de madrugada) e “funfou” que foi uma beleza. Agora mais uma coisa, que ainda não fiz devido o tempo: este SimpleIndexConverter, poderá ser único na aplicação? tipo, se num form eu tiver um select de cidades e outro de relacionamento, quando eu submeter o formulário o converter vai converter os dois objetos para mim e passar, por exemplo, para meu usuario.cidade e usuario.relacionamento os objetos correspondentes?
    Obrigado pela atenção!

  46. Sim, o faces cria uma instância do converter para cada componente, ou seja, eles são thread-safe e podem ser utilizados em vários componentes na mesma página sem qualquer problema.

  47. Olá Rafael,
    não sou muito de comentar nos blogs,
    mas dessa vez parabéns , consegui depois de muito tentar e olhar por ai,
    fazer esse bendito converter ^^ , no meu caso estou usando o primefaces,
    no começo deu erro falando que não encontrava o Converter,depois configurei no faces-config e tiver que usar a tag , só após isso rodou perfeitamente.

    Obrigado

  48. esse teu converter tosco não tá funcionando merda nenhuma.
    java.lang.NullPointerException
    at javax.faces.component.UIComponentBase$AttributesMap.put(UIComponentBase.java:2374)
    at javax.faces.component.UIComponentBase$AttributesMap.put(UIComponentBase.java:2283)
    at br.com.soc.view.SimpleEntityConverter.addAttribute(SimpleEntityConverter.java:42)
    at br.com.soc.view.SimpleEntityConverter.getAsString(SimpleEntityConverter.java:29)
    at org.richfaces.component.util.InputUtils.getInputValue(InputUtils.java:174)
    at org.richfaces.component.util.InputUtils.getInputValue(InputUtils.java:157)

  49. Boa noite!
    Olá Rafael, você Deus??
    hehe PARABÉNS pelo post, muito bom…
    eu estava tentando fazer algo assim mas não estava conseguindo, agora bombou aqui!
    valeu!

  50. Rafael,

    Cara, vc deveria dar uma melhorada nesse site(blog) seu, essa parada de exibir o body, como se estivesse vendo uma folha A4 é foda, tah parecendo uma coluna de uma table essa parada, ainda mais falando a respeito de CÓDIGO, fica inviável ler os códigos na página, tem que copiar e colar em outro lugar, identar e dai conseguir lê….

    Usa essa tela toda rapaz, até parece que tah escrevendo pra usuário final, mais preciso windowsUser….

  51. cara os metodos equals e hashCode eu gero na mão ou pela ide??

  52. Kristiano,

    Tanto faz, mas normalmente geramos os métodos pela IDE mesmo. Contudo, você precisa ter ciência de qual é a identidade da sua entidade. Normalmente é apenas o atributo Id (PK).

  53. Oi Rafael,

    Excelente post! Bem escrito!

    Eu implementei aqui o exemplo do SimpleEntityConverter associado a um e estou usando para mudar os valores de outros componentes quando mudar o valor do .

    Porém, quando realizo o debug no código do converter (baseado no SimpleEntityConverter) consigo recuperar o objeto relacionado ao atributo setado anteriormente, porém, os valores das propriedades deste objeto recuperado estão nulos. o que pode está acontencendo?

  54. Olá Adolfo,

    Não consegui entender bem o seu problema. Poderia me mostrar o código fonte do seu converter?

  55. Rafael, obrigado pela atenção!

    Conseguimos encontrar a solução: Foi fazer com que o bean utilizado para compor a lista de um SelectOneMenu (primefaces) implementasse a classe Serializable.

    O problema era porque o objeto recuperado pelo método getAsObject estava vindo com as propriedades nulas, no código abaixo:

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) {
    if (value != null) {
    return this.getAttributesFrom(component).get(value);
    }
    return null;
    }

  56. Rafael Ponte, essa solução se enceixará perfeitamente em meu projeto.
    Já configurei o faces-config.
    Estou tentando usar esse conversor dentro de uma tag do primeFaces:

    $p:wizard$

    $p:tab$

    $p:selectManyCheckbox id=”especialidade” value=”#{funcionarioBean.funcionario.especialidades}” converter=”simpleIndexConverter”$

    $f:selectItems value=”#{funcionarioBean.especialidades}” var=”esp” itemLabel=”#{esp.tipo}” itemValue=”#{esp}” /$

    $/p:selectManyCheckbox$

    $/p:tab$

    $/p:wizard$

    Então quando tento trocar de tabela não acontece nada, nem ocorre erro…

    Minha classe Especialidade é simples, só contem um “id” e o “tipo”

    E minha classe Funcionario contem um “Set especialidades”.

  57. Estou utilizando VIEW, pq o se utilizar o REQUEST perco as informações do funcionario em questão.
    Me manda seu email pra eu te mandar os codigos.

  58. Testei o SimpleEntityConverter e funcionou.
    Mas minha intenção é utilizar o SimpleIndexConverter.
    Debuguei o código do conversor e da pau no iterator na linha de código “item = iter.next();” (linha 129, aproximadamente). Já estou programando pensando em não sobrecarregar o Servidor, por isso gostaria de utilizar este converter.

  59. O pau que citei é SOURCE NOT FOUND (tomcat-coyote.jar).
    Mas esse .jar existe em minhas bibliotecas. Será que é algum problema de compatibilidade?
    Para exclarecer melhor: Estou um FILTRO para gerenciar as conexões.
    Então a cada NEXT do WIZARD esse filtro é acionado. Por isso meu escopo do managed bean é VIEW. Caso contrario o filtro não me retornaria os dados da TAB (WIZARD) anterior.

  60. Oi Wesley,

    Qual erro ocorre? Qual a exceção?

    Lembro que esse converter bugava com o MyFaces (tem até um comentário nesse post falando sobre isso), logo era preciso fazer uma pequena correção.

  61. E ae Rafael, tranquilo?

    É, a pane esta no carregamento da pagina, quando é chamado o método getAsString() do conversor.
    Nosso amigo informou que a solução foi subustituir o conteudo por:
    if (value == null)
    return “”;
    index++;
    return String.valueOf(index);

    Substitui o “” por null e depois por “”, continuou aparecendo a página informando SOURCE NOT FOUND. As primeiras linhas são:

    // Compiled from InputRenderer.java (version 1.5 : 49.0, super bit)
    public abstract class org.primefaces.renderkit.InputRenderer extends org.primefaces.renderkit.CoreRenderer {

    // Method descriptor #74 ()V
    // Stack: 1, Locals: 1
    public InputRenderer();
    0 aload_0 [this]
    1 invokespecial org.primefaces.renderkit.CoreRenderer() [1]
    4 return
    Line numbers:
    [pc: 0, line: 33]
    Local variable table:
    [pc: 0, pc: 5] local: this index: 0 type: org.primefaces.renderkit.InputRenderer

    // Method descriptor #81 (Ljavax/faces/context/FacesContext;Ljavax/faces/component/UIInput;)Ljava/util/List;
    // Signature: (Ljavax/faces/context/FacesContext;Ljavax/faces/component/UIInput;)Ljava/util/List;
    // Stack: 9, Locals: 11
    protected java.util.List getSelectItems(javax.faces.context.FacesContext context, javax.faces.component.UIInput component);
    0 new java.util.ArrayList [2]
    3 dup
    4 invokespecial java.util.ArrayList() [3]
    7 astore_3 [selectItems]
    8 aload_2 [component]
    9 invokevirtual javax.faces.component.UIInput.getChildren() : java.util.List [4]
    12 invokeinterface java.util.List.iterator() : java.util.Iterator [5] [nargs: 1]
    17 astore 4 [i$]

    Vc nunca usou esse conversor juntamente com o Primefaces? O interessante é que com o SimpleEntityConverter funciona numa boa..

  62. Olá Rafael, primeiramente gostaria de agradecer pelos seus tutoriais, são de grande ajuda, porem neste exemplo estou com um problema, que não sei o que pode estar acontecendo, meu converter aparentemente funcionou, porem na hora de salvar o objeto, o objeto que deveria ter sido populado esta null, o que pode estar aconteçendo??

    Tenho um objeto jogador, e escolho um Municipio pra ele com um h:selectOneMenu onde tem um converter para Municipio, porem ele fica null, o que pode ser?

  63. Bom dia Rafael, ao utilizar o conversor genérico, dá um erro de conversão na linha 114 (da tela), diz que não pode converter meu Bean para SelectItem, você sabe o que pode ser e como proceder?

  64. Olá Rodrigo,

    Linha 114? Está falando de que converter? SimpleIndexConverter?

    Poderia colocar o erro principal da exceção? Assim facilitaria o entendimento do problema!

  65. Olá Rafael, o erro estava acontecendo na linha 114 do SimpleIndexConverter mesmo (item = iter.next();). Eu estava colocando um ArrayList do tipo da minha entidade (ControleProcesso) no SelectOneMenu e dava esse erro no converter. Modifiquei e coloquei um ArrayList do tipo SelectItem, setando o label e o value dos elementos dela de acordo com a lista da minha entidade. Agora está funcionando perfeitamente. Obrigado pelo feedback, valeu!

  66. Ótimo post cara! mas me tira uma dúvida que está acontecendo comigo:
    no formulário onde você coloca o código

    o valor do itemLabel é o que chega como valor no getAsObject do Converter, não? Mas você está mapeando o objeto pelo Id e não pelo value que chega, no caso o.nome no getAsString. Pelo menos comigo está acontecendo isso. Gostaria de usar o id mesmo, mas seu eu uso o id no itemLabel vai apenas aparecer os números no componente. Queria o id porque gostaria de buscar no banco de dados. Tem alguma sugestão?
    Agradecido.

  67. Oi Luis,

    O código não apareceu aqui para mim. Que componente você está utilizando exatamente?

  68. Olá, primeiramente parabéns pelo post.
    Fiz do jeito que você falou e gostaria de saber se é necessário mais alguma coisa, pois a minha pagina *.xhtml não renderiza mais, aparece o seguinte erro:

    jun 06, 2015 5:23:51 PM com.sun.faces.application.view.FaceletViewHandlingStrategy handleRenderException
    GRAVE: Error Rendering View[/novaManutencao.xhtml]
    java.lang.ClassCastException: java.lang.Long cannot be cast to br.com.tcc.util.SimpleEntityConverter$BaseEntity
    at br.com.tcc.util.SimpleEntityConverter.getAsString(SimpleEntityConverter.java:26)
    at org.primefaces.renderkit.InputRenderer.getOptionAsString(InputRenderer.java:152)
    at org.primefaces.component.selectonemenu.SelectOneMenuRenderer.encodeOption(SelectOneMenuRenderer.java:360)
    at org.primefaces.component.selectonemenu.SelectOneMenuRenderer.encodeSelectItems(SelectOneMenuRenderer.java:346)
    at org.primefaces.component.selectonemenu.SelectOneMenuRenderer.encodeInput(SelectOneMenuRenderer.java:140)
    at org.primefaces.component.selectonemenu.SelectOneMenuRenderer.encodeMarkup(SelectOneMenuRenderer.java:93)
    at org.primefaces.component.selectonemenu.SelectOneMenuRenderer.encodeEnd(SelectOneMenuRenderer.java:67)
    at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:919)
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1863)
    at org.primefaces.component.panelgrid.PanelGridRenderer.encodeDynamicBody(PanelGridRenderer.java:131)
    at org.primefaces.component.panelgrid.PanelGridRenderer.encodeTableBody(PanelGridRenderer.java:102)
    at org.primefaces.component.panelgrid.PanelGridRenderer.encodeTableLayout(PanelGridRenderer.java:65)
    at org.primefaces.component.panelgrid.PanelGridRenderer.encodeEnd(PanelGridRenderer.java:37)
    at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:919)
    at org.primefaces.renderkit.CoreRenderer.renderChild(CoreRenderer.java:88)
    at org.primefaces.renderkit.CoreRenderer.renderChildren(CoreRenderer.java:71)
    at org.primefaces.component.dialog.DialogRenderer.encodeContent(DialogRenderer.java:180)
    at org.primefaces.component.dialog.DialogRenderer.encodeMarkup(DialogRenderer.java:109)
    at org.primefaces.component.dialog.DialogRenderer.encodeEnd(DialogRenderer.java:47)
    at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:919)
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1863)
    at javax.faces.render.Renderer.encodeChildren(Renderer.java:176)
    at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:889)
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1856)
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1859)
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1859)
    at com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:456)
    at com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:133)
    at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:337)
    at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:120)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
    at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:219)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:647)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:617)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:668)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1521)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1478)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

  69. Olá,

    O erro que está acontecendo é este aqui:
    java.lang.ClassCastException: java.lang.Long cannot be cast to br.com.tcc.util.SimpleEntityConverter$BaseEntity

    Ele se refere a problemas de casting na sua entidade. Você deve estar tentando fazer o casting da entidade em si (do tipo BaseEntity) para o tipo Long que é o tipo do atributo id da entidade. Verifique se este não é problema na linha 26 do seu converter:

    at br.com.tcc.util.SimpleEntityConverter.getAsString(SimpleEntityConverter.java:26)

    Normalmente a exceção (erro) lançado mostra a maioria se não todas as informações que você precisa para analisar e encontrar o problema, no caso acima ele disse exatamente em que linha o problema de casting ocorreu!

    Um abraço!

  70. Simplesmente fantástico e muito bem explicado 😀
    Parabéns!!

  71. Olá R Ponte, bom dia!
    Alguns dias atrás implementei sua ideia e funcionou perfeitamente.
    O problema é que fui tentar implementar novamente e não estou
    conseguindo a 3 dias. Acontece que o converter (solução SimpleEntityConverter)
    não exibe os itens carregados, ou seja, é feita a solicitação, os dados são
    encontrados e retornados, mas por algum motivo que desconheço
    o selectOneMenu não está sendo populado. E o pior de tudo é que não
    é apresentado nenhum erro no console do eclipse.
    Você tem ideia do que possa ser? Poderia me ajudar?
    Meu caro, te agradeço muito… Obrigado!

  72. Oi,

    Posso ajudar sim. Mas é bem estranho a lista estar sendo retornada e a combobox não estar sendo construída. Não aparece nenhum item na combo ou ela aparece com os itens vazios?

    Outra dúvida, qual o escopo do seu managed bean? Coloca ele como @SessionScoped e ver no que dá!

    O que seu converter tem demais? Mudou algo nele?

    Um abraço!

  73. Obrigado por responder meu caro!
    Respondendo suas duvidas:
    1. O único item que aparece no selectOneMenu é este:

    2. O ManagedBean já está @SessionEscoped

    3. Meu Converter está exatamente igual o tratado no seu exemplo (SimpleEntityConverter). Nada foi alterado. :-/

    Já viu algo parecido?

  74. ** Corrigindo item que aparece no selectOneMenu:
    Selecione uma empresa

  75. Então, o único item q aparece no selectOneMenu é:
    <f:selectItem itemValue=”” itemLabel=”Selecione uma empresa”/ >

    Quando eu retirei o atributo converter=”simpleEntityConverter” do selectOneMenu nada mudou.

    Com isso parece que o que está acontecendo é que o selectOneMenu não está conseguindo a conexão com o Converter, correto?

  76. Segue o código da page.xhtml

    <h:selectOneMenu id=”empresa”
    value=”#{empresaController.selectedEmpresa}” converter=”#{simpleEntityConverter}”
    required=”true” requiredMessage=”Valor é obrigatório”>

    <f:selectItem itemValue=”” itemLabel=”Selecione uma empresa”/>
    <f:selectItems value=”#{empresaController.empresas}” var=”o” itemLabel=”#{o.nome}” itemValue=”#{o}” />
    </h:selectOneMenu>

  77. Olá,

    Mesmo removendo o converter o problema persiste? Humm… tem certeza que existem itens nessa lista?

    Vamos tentar voltar ao básico e ver se ao menos a combobox é prenchida corretamente. Mude seu código para:

    É precisa identificar se o problema está de fato no converter ou na lista retornada (pode ser erro de lógica no método getter, na consulta com o banco ou outra coisa).

  78. Aff, WordPress lexerento!

    f:selectItems value=”#{empresaController.empresas}” var=”o” itemLabel=”#{o.nome}” itemValue=”#{o.codigo}”

  79. Fiz a mudança e nada, continua na mesma. Fiz também um outro teste. Em vez de:
    &gl;h:selectOneMenu id=”empresa” value=”#{empresaController.selectedEmpresa}” converter=”#{simpleEntityConverter}” />

    alterei a chamada/referencia para: (simpleEntityConverter123 não existe)
    &gl;h:selectOneMenu id=”empresa” value=”#{empresaController.selectedEmpresa}” converter=”#{simpleEntityConverter123}” />

    e o resultado foi que: não houve resultado, ainda continua sem funcionar.
    Conclusão é que por algum motivo o componente selectOneMenu não está se conectando com o simpleEntityConverter

  80. Oi,

    Se você removeu o converter e simplificou o código como falei e ainda assim a combo não foi montada então o problema não está no converter.

    Novamente, tem certeza que a EL #{empresaController.empresas} está retornando as empresas do banco? Jogue numa h:dataTable ou itere e veja se existem dados de fato nessa lista.

  81. Então, fiz umas mudanças aqui e deixei de lado o exemplo utilizando ArrayList estatico e coloquei numa aplicação que tenho com banco de dados.
    Puxei os dados do banco e a p:dataTable é populada com a mesma list do selectOneMenu.
    A p:dataTable funcionou redondinho, mas o nosso amigo selectOneMenu não.

    Rafael, não sei nem como te agradecer pela sua atenção!

  82. Anderson,

    O problema está bem bizarro mesmo. Vamos fazer o seguinte, vamos voltar para o mais básico ainda, ok?

    Crie uma nova página (xhtml) na qual possui somente um h:selectOneMenu dentro e NAO use o converter. Este componente vai apontar para um novo managed bean na qual vai possuir somente um único método getEmpresas() na qual vai retornar uma lista hard-coded com algumas empresas. Pode fazer isso?

    Se der certo, vamos para o segundo passo… ainda na mesma página, repinta o mesmo h:selectOneMenu MAS desta vez COM o converter.

    No final a página terá 2 combos: uma sem converter e outra com converter.

    Enfim, deve ter algo muito fora do comum no seu projeto. Precisamos identificar, e uma boa prática é isolando os componentes e o problema até achar a causa.

    Um abraço!

  83. Olá Rafael, bom dia!
    Cara, segui com sua ideia de ir isolando os componentes e descobri o problema.
    O componente do selectOneMenu que eu estava usando era do
    primefaces (p:). Por algum motivo que ainda vou ver melhor está causando esse bug. E de fato, como eu havia
    lhe falado antes, a List estava carregada, o problema era que o
    p:selectOneMenu só exibia o primeiro item que é estático
    (f:selectItem itemValue="" itemLabel="Selecione uma empresa" ) dando a
    impressão que a List não era carregada rsrsrs.
    Meu caro, não sei como te agradecer. Muito obrigado mesmo!
    Um dia a gente se ver por aí, vlw.
    [ ]s

  84. Opa! Que bom que deu certo e você descobriu o problema! Sei bem como é ficar preso num detalhe desse!

    De qualquer forma, recomendo você entrar no grupo de discussão da #JavaSF a fim de tirar suas dúvidas e aprender com as dúvidas e experiências dos demais membros. Com certeza lá o pessoal vai te responder bem mais rápido do que eu!

    Um abraço!

  85. Rafael, obrigado por compartilhar!! Passei por esse problema com o JSF – a qual fazendo um primeiro projeto – e tive problemas até pra entender qual era o problema
    de simplesmente não persistir. Cavando um pouco mais, felizmente, consegui encontrar seu material. Deu tudo certo usando o SimpleEntityConverter, obrigado!

  86. Oi, Rafael!

    Preciso fazer alguma modificação para fazer o converter setar um valor vindo do banco de dados ao editar o registro?
    No meu caso, funcionou apenas na gravação.

    Att,
    Luiz Henrique Santana

  87. Oi Luiz,

    Não entendi sua pergunta. Mas não precisa alterar na nada no converter, ele pode ser usado em qualquer tipo de tela e componente visual (normalmente o mais comum é combobox).

  88. Fala Rafael tudo bom?
    Estou tendo um problema com o converte em um SelectManyCheckBox com um Banco de dados.

    Quando eu coloco o public Long getId(){ return new Long(MeuID); }
    Está com sublinhando o getId com esta mensagem:
    “For propert-based access both setter and getter should be present”

  89. Fabiano,

    Creio que você não criou os métodos getters e setters da sua propriedade corretamente. Crie o getId() e setId() pela sua IDE.

  90. Olá Rafael muito obrigado pela dica,

    pode me ajudar?

    No meu caso o Relacionamento de 1-1 funcionou perfeitamente, porém agora preciso utilizar o manytomany, você pode me ajudar com o “save”?

    public String save() {
    Servidor coordenador = servidorServico.find(coordenadorId);
    System.out.println(coordenador);
    value.setCoordenador(coordenador); // ——-até aqui blz —– consigo salvar um coordenador para um projeto —– mas para listar vários participantes de um projeto estou com problemas

    /* aqui aparece o erro
    List servidoresParticipantes = (List) servidorServico.find(this.servidoresPart);
    System.out.println(servidoresParticipantes);
    value.setServidoresParticipantes((List) projetoServico.find(servidoresPart));
    */

    projetoServico.create(value);
    reset();
    return null;
    }

  91. Olá,

    Primeiro de tudo, qual o erro? Sem saber o erro não tenho muito como te ajudar. De qualquer forma, olhando o código creio que o método find() retorne uma entidade e não uma lista. Seu casting aí deve ser o problema.

  92. Cara tudo bem? Ótimo post o seu, parabéns!
    Infelizmente estou tendo um erro no meu converter.

    java.lang.ClassCastException: model.Curso cannot be cast to testes.BaseEntity
    converter.SimpleEntityConverter.getAsString(SimpleEntityConverter.java:29)

    Pelo que entendi o erro esta na linha 29 do meu SimpleEntityConverter, que é esta:
    BaseEntity entity = (BaseEntity) value;

    Mas não faço ideia o por que. Ainda sou novo na programação e não consegui resolver. Se puder me ajudar agradeço 😉

  93. Eu diria que sua classe Curso não implementa a interface BaseEntity. Ao menos é o que o erro diz: ClassCastException. Verifique e faça a classe implementar a interface.

  94. Estava implementando. Mesmo assim não deu certo
    Por fim achei um outro conversor (sem ser genérico, um próprio para a classe Curso) e funcionou! Mas depois vou testar esse novamente para ver se acho o erro e posto aqui

    Agradeço por tentar ajudar e parabéns pelo ótimo trabalho =)

Deixe um comentário