Para você: EntityConverter para qualquer entidade e tipo de Id

51 respostas
F

Depois de ter encontrado a solução SimpleEntityConverter em http://www.rponte.com.br/tag/entity-converter/, resolvi melhorá-la, deixando-a ainda mais genérica. O próprio autor diz: "(…) É 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 (…).

1 - Não há necessidade de implementar a interface BaseEntity
2 - O id da classe pode ser String, Integer, etc. e não apenas Long como em SimpleEntityConverter, pois a classe identifica o retorno pela anotação @Id. Essa foi uma das motivações que me levaram a melhorar SimpleEntityConverter, pois algumas entidades de nossos sistemas não possuem id do tipo Long, mas String.

package net.metha.jsf.converter;

import java.lang.reflect.Field;

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

/**
 * Converter para entidades JPA. 
 * Baseia-se na anotação @Id para identificar o atributo que representa a identidade da entidade.  
 * @author Flávio Henrique 
 * @version 1.0 
 * @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;
	}

	private Class<?> getClazz(FacesContext facesContext, UIComponent component) {
		return component.getValueExpression("value").getType(
				facesContext.getELContext());
	}

	public String getId(Class<?> clazz, Object obj) throws SecurityException,
			NoSuchFieldException, IllegalArgumentException,
			IllegalAccessException {
		for (Field field : clazz.getDeclaredFields()) {
			if ((field.getAnnotation(Id.class)) != null) {
				Field privateField = clazz.getDeclaredField(field.getName());
				privateField.setAccessible(true);
				if (privateField.get(clazz.cast(obj)) != null) {
					return (String)field.getType()
							.cast(privateField.get(clazz.cast(obj))).toString();
				} else {
					return null;
				}
			}
		}
		return null;
	}
}

Registre o converter em faces-config.xml:

entityConverter
net.metha.jsf.converter.EntityConverter

Use e abuse:

<h:selectOneMenu id=“comboRestaurantes"
value=”#{consumoMB.restauranteOperacao}" required="true"
label=“Restaurante” converter=“entityConverter”>

10/02/2011 -> RETIFICANDO: O CÓDIGO ACIMA NÃO É THREADSAFE, USAR DESTA FORMA:

<h:selectOneMenu id="comboRestaurantes"
value="#{consumoMB.restauranteOperacao}" required="true"
label="Restaurante">
<f:converter converterId="entityConverter"/>
</selectOneMenu>

Esta solução está sendo usada em ambiente de produção.

Atenciosamente,

Flávio Henrique de Souza Almeida

51 Respostas

A

Bacana. Pro pessoal que usa o JBoss Seam isso não é necessário. Basta usar a tag s:convertEntity.

Mais uma coisinha: com @EmbeddedId sua solução não funcionará, bem como subclasses cujo id esteja na superclasse.

F

Obrigado pelas sugestões.

Estamos cientes do s:converEntity do Seam, mas como a versão 2.0 não suporta CDI (out-of-the-box), estamos aguardando a versão 3.0.

Sobre o @EmbeddedId: acredito que eu não tenha dificuldade em adicionar esta funcionalidade.

Sobre herança: farei a alteração.

F

Suporte à herança adicionado.

public String getId(Class<?> clazz, Object obj) throws SecurityException,
			NoSuchFieldException, IllegalArgumentException,
			IllegalAccessException {
		Class<?> classAnotada = clazz;
		if(clazz.getSuperclass() != Object.class){
			classAnotada = clazz.getSuperclass();		
		}
		for (Field field : classAnotada.getDeclaredFields()) {
			if ((field.getAnnotation(Id.class)) != null) {
				Field privateField = classAnotada.getDeclaredField(field.getName());
				privateField.setAccessible(true);
				if (privateField.get(clazz.cast(obj)) != null) {
					return (String) field.getType()
							.cast(privateField.get(clazz.cast(obj))).toString();
				} else {
					return null;
				}
			}
		}
		return null;
	}
G

} catch (SecurityException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); }

Isso é um dos piores erros que um programador pode fazer quando trabalha com exceptions. :shock:

http://today.java.net/article/2006/04/04/exception-handling-antipatterns

F

Podemos gravar em um log essas exceções ou fazer com que getId() lance uma exceção personalizada (EntityConverterException).

R

Olá Flavio,

Muito bacana a solução e fico feliz que você tenha disponibilizado a mesma no GUJ. Acredito que não exista lugar melhor para isso! :slight_smile:

Como já é sábido por você, o JBoss Seam se utiliza dessa mesma solução, através do s:entityConvert, e provavelmente o código dele tenha coisas bem interessantes para se observar se tratando de problemas comuns, como o problema de herança já corrigido por você.

Eu havia pensado nessa solução também, mas ela acabou ficando fora do post. Contudo, o mais interessante de tudo isso são estes tipos de iniciativas em compartilhar conhecimento e código com a comunidade.

Enfim, como o garcia-jj comentou, melhore o tratamento de exceções, pois ignora-las é uma má prática. E claro, se possível disponibilize o código no GitHub :slight_smile:

Um abraço e parabéns novamente.

G

Usar o github é excelente, pois muitas pessoas podem fazer um fork e contribuir.

F

Adicionei suporte à anotação @EmbeddedId e corrigi um problema na hierarquia de classes.

package net.metha.jsf.converter;

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;
	} 
}
F

Estamos usando em produção EntityConverter e até agora não tivemos problemas.
A diferença básica entre EntityConverter e o s:ConvertEntity do Seam é a seguinte:

1 - s:ConvertyEntity obtém o id da entidade e realiza a procura no banco de dados utilizando JPA. Eles resolvem o problema da unidade de persistência, permitindo que o usuário defina este atributo na própria tag quando necessário. Acho essa solução interessante, ficando ainda melhor quando temos um cache de segundo nível como ehcache.

2- EntityConverter, assim como SimpleEntityConverter, altera o estado do componente no lado do servidor. A vantagem é de não precisamos acessar o EntityManager dentro do converter, mas há um custo de processamento, que em nossos sistemas, até o momento, é imperceptível.

Eu poderia alterar o código e usar a mesma estratégia do s:ConvertEntity (o código de infraestrutura já permite isso), mas se isso fosse necessário, eu utilizaria o s:ConvertEntity e não o EntityConverter.

O que eu gostaria deixar bem claro aqui é que EntityConverter foi criado pensando nas aplicações que desenvolvo, desta forma, será necessário que cada um avalie qual tipo de conversor de entidade utilizar.

Abraço

A

Muito bom, meus parabéns! Tanto pelas melhorias como pela iniciativa de compartilhar suas ideias conosco.

F

Ataxexe sugeriu a utilização de uma biblioteca de reflexão para deixar o código mais legível. A biblioteca escolhida foi a Trugger 2.7.0 que pode ser baixada em:

O método getId() foi reescrito com base no código enviado pelo colega. Reparem que não é mais necessário o método getHierarquiaDeClasses() nem o parâmetro da classe.

Para o código ficar ainda mais rápido, será necessário limar o método getClazz (evoca EL), mas eu ainda preciso dele no método getAsString() devido a necessidade de sabermos qual a classe do objeto que chega até o converter para podermos gravá-lo dentro do map. Como ainda estou conhecendo a biblioteca trugger, acredito que ela tenha a chave para esta resposta.

Ataxexe, você vê alguma solução?

Segue o código alterado. Não esqueçam de adicionar trugger-2.7.0.jar
OBS: eu fiz um teste premilinar e funcionou. Amanhã, farei outro teste colocarei em produção.

package net.metha.jsf.converter;

import static net.sf.trugger.element.Elements.element;
import static net.sf.trugger.reflection.ReflectionPredicates.annotatedWith;

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. Também
 * as anotações nas super classes.
 * 
 * @author Flávio Henrique
 * @version 1.0.4
 * @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;
		
				id = this.getId(obj);
				if (id == null) {
					id = "";
				}
				id = id.trim();
				component.getAttributes().put(id,
						getClazz(ctx, component).cast(obj));
				return id;
		}
		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 Object obj
	 * 
	 * @return String
	 */
	public String getId(Object obj) {
		Object idValue = element()
				.thatMatches(
						annotatedWith(Id.class).or(
								annotatedWith(EmbeddedId.class))).in(obj)
				.value();
		return String.valueOf(idValue);
	}
}
F

O desempenho está muito bom.

Coloquei mais um sistema nosso utilizando esta classe. Para os managedBeans em escopo de visão, o desempenho é melhor ainda.

F

Este converter não funciona com rich:combobox. Por quê? Porque esta tag não usa selectedItens internamente, ou seja, é derivada da suggestionbox. Parece que isso foi resolvido no richfaces 4.

Assim que eu realizar o teste, eu posto aqui.

Abraço

H

Boa tarde,

Estou usando este converter mas não consigo obter o resultado esperado.
No método getAsObject() recebo um null. Debuguei o método getAsString() e os objetos são inseridos no map normalmente, mas quando preciso obter no método getAsObject() recebo um null.

Segue o código:

public String getAsString(FacesContext ctx, UIComponent component, Object obj)
	{
		if (obj != null && !"".equals(obj))
		{
			String id;
			
			id = this.getId(obj);
			if (id == null)
			{
				id = "";
			}
			id = id.trim();
			//Aqui insere normal no map
			component.getAttributes().put(id, getClazz(ctx, component).cast(obj));
			return id;
		}
		return null;
	}
public Object getAsObject(FacesContext ctx, UIComponent component, String value)
	{
		if (value != null)
		{
			// Aqui retorna null sempre
			return component.getAttributes().get(value);
		}
		return null;
	}

O registro do converter no faces-config:

<converter>		
       <converter-id>entityConverter</converter-id>
       <converter-class>br.com.telefonica.indra.gestaodeativos.converters.EntityConverter</converter-class>
</converter>

Página:

<h:selectOneMenu id="perfil" value="#{usuarioBean.perfil}" converter="entityConverter">

Alguma idéia?

Preciso colocar algo mais do meu código?

Atte.

Gustavo Belloni Metzner

F

HarryPodre, como você populou sua lista de selectedItens? Coloca aqui o código para que eu possa analisar.
Abraço

H

Flavio Almeida:
HarryPodre, como você populou sua lista de selectedItens? Coloca aqui o código para que eu possa analisar.
Abraço

Flávio, este trecho do código ajuda?

List<SelectItem> itens = new ArrayList<SelectItem>();
		itens.add(new SelectItem(null, "Selecione"));
		
		for (Perfil perfil : perfis)
		{
			itens.add(new SelectItem(perfil, perfil.getDescricao()));
		}

Valeu!

F

Hoje eu estou bem atarefado, mas responderei com mais calma à noite.

Faça assim:

itens.add(new SelectItem(null, “”)); // string em branco no lugar de “Selecione”

Perfil tem a anotação @Id ou @EmbeddableId?

Estamos usando este converter em produção sem problemas, mas estou interessado em ajudar você a resolver seu problema.

F

Você está usando a tag <f:selectItems> ?

Veja um exemplo:

<h:selectOneMenu id="papelUsuario" value="#{usuarioController.usuario.papel}" disabled="#{usuarioController.formStatus.consultando}" label="Papel" required="true" converter="entityConverter">
   <f:selectItems value="#{usuarioController.listaComboPapeis}" />
</h:selectOneMenu>

Lucas Sorrentino vai te ajudar!

H

Estou sim.

<h:selectOneMenu id="perfil" value="#{usuarioBean.perfil}" converter="entityConverter">
    <f:selectItems value="#{usuarioBean.perfis}"/>			            			            
</h:selectOneMenu>
H

Flavio Almeida:
Hoje eu estou bem atarefado, mas responderei com mais calma à noite.

Faça assim:

itens.add(new SelectItem(null, “”)); // string em branco no lugar de “Selecione”

Perfil tem a anotação @Id ou @EmbeddableId?

Estamos usando este converter em produção sem problemas, mas estou interessado em ajudar você a resolver seu problema.

O Perfil tem a anotação @Id.

Colocar uma string em branco só vai remover o “Selecione” do combo, mas o erro vai continuar.
Vou continuar debugando aqui, acho que deixei escapar algo rs.

Valeu!

L

cara, esse erro acontece quando você seleciona qualquer item da combo?
A combo renderiza direitinho, com todos os itens?

H

Lucas Sorrentino:
cara, esse erro acontece quando você seleciona qualquer item da combo?
A combo renderiza direitinho, com todos os itens?

Então Lucas, ele renderiza normal, com todos os itens e o erro acontece com qualquer item selecionado.
Vou fazer um teste aqui, acho que o problema é no meu combo, to fazendo algo errado… assim que testar ja posto algo.

Valeu!

L

Harry,
essa lista que você ta passando pra sua página é uma lista de select item? Esse seu usuarioBean.perfis?

H

É sim Lucas, é uma lista de select item, gero ela assim:

List<SelectItem> itens = new ArrayList<SelectItem>();
   itens.add(new SelectItem(null, "Selecione"));
		
   for (Perfil perfil : perfis)
   {
       itens.add(new SelectItem(perfil, perfil.getDescricao()));
   }
   return itens;

Esse combo não é required, mas resolvi deixa-lo para ver o que acontece. Mesmo que você selecione um item ele não entende que foi selecionado, exibe uma mensagem que o campo perfil é obrigatório, como se o meu equals não tivesse sido implementado, mas foi.
Enfim, já achei o erro mas não sei como solucionar, pois aparentemente está correto.

Tenho uma outra aplicação com um combo, mas que possui um converter especifico que funciona perfeitamente.
Refiz o combo baseado neste que funciona, exceto o converter pois quero utilizar o generico e mesmo assim é como se não tivesse selecionado nada no combo.

Fui claro?

Só percebi isso pois tentei utilizar o combo como required.

H

Esquece o que falei no ultimo post, to viajando aqui…rs
Ele não reconhece que o combo foi preenchido pq o converter só retorna null no método getAsObject() rs.

A ideia do converter é colocar o objeto num map e depois recuperar pela chave que é o id, certo ?
Quando vou recupar o map está vazio:

// só retorna null  
  component.getAttributes().get(value);

Sei lá, vou recomeçar esse combo do zero, refazer tudo pra ver se não to fazendo besteira rs.

Valeu pela ajuda!

H

Lucas e Flavio, fiz um projetinho do zero aqui e o converter funcionou perfeitamente.
Como suspeitava o problema é no meu código, vou ver o que consigo e posto aqui.

Muito obrigado pela ajuda!

F

Que bom que funcionou, já estava achando que era algum problema não identificado antes no código.
Abraço

H

Só uma dúvida, vcs usam jsf com o que? richfaces, facelets?

H

Ah, descobri o motivo de não funcionar rs.

Achei isso no web.xml

<context-param>
   <param-name>facelets.BUILD_BEFORE_RESTORE</param-name>
   <param-value>true</param-value>
</context-param>

Estava como ‘true’ então passei pra ‘false’:

<context-param>
  <param-name>facelets.BUILD_BEFORE_RESTORE</param-name>
  <param-value>false</param-value>
</context-param>

Como meu acesso a internet é limitado não pude procurar pra que realmente serve essa configuração.

Agora ta funcionando legal.

Valeu pela ajuda!

F

HarryPodre, muito bem apontado. Realmente, usamos essa opção com o valor “false” em nossas aplicações.

http://www.jsfcentral.com/listings/A23139;jsessionid=B10FB0FDD00C333B6F6D884FD05D25D5.tomcat28?link

C

:smiley: Flavio Almeida !!!

Meus Parabéns pela solução, pelo visto tivemos pesquisando nos mesmos lugares. rsrs (http://www.rponte.com.br)
Eu estava muito desapontado em ter que fazer um converter para cada entidade do meu modelo.

A priori, está funcionando bem. vou continuar os testes, porém não estou usando a versão com reflex, para evitar o uso de mais uma biblioteca.
O que acha ?
No que for possível te darei feedback.

Forte abraço. Continue assim, a nosso comunidade é forte por isso. :wink:

F

Não vejo problema algum. Eu prefiro a versão da biblioteca, porque deixa o código mais claro. Se não estou enganado, há um sistema nosso rodando a versão sem a biblioteca de reflexão há um bom tempo sem ter problema.

O feedback é muito importante, acredite, ainda mais que utilizamos essa classe em ambiente de produção.

Qualquer dúvida é só postar aqui.

Abraço

C

Flavio Almeida:
O desempenho está muito bom.

Coloquei mais um sistema nosso utilizando esta classe. Para os managedBeans em escopo de visão, o desempenho é melhor ainda.

Flavio preciso da tua ajuda,
estou com um problema relacionado a atualização do selectOneMenu mesmo depois que atualizo o campo do meu managedBean, estou usando seu converter.

Abri um tópico hoje: http://www.guj.com.br/java/237628-problema-com-hselectonemenu—projeto-com-jsf-2-e-primefaces

Pode me dar uma ajuda com isso ? Obrigado … abs…

W

Parabens Flavio Almeida esse conversor é muito bom mesmo,
mas eu to com um probleminha aqui talvez vc conheça uma solução.
Estou usando esse conversor dentro um componente composto, por causa disso ou sei la o que, no metodo

private Class<?> getClazz(FacesContext facesContext, UIComponent component) { return component.getValueExpression("value").getType(facesContext.getELContext()); }

ele está me retornando null, não sei se é porque esta trocando o facesContext ou coisa parecida.
Mas aí a duvida tem como eu consertar isso ou passar a classe que eu desejo para o conversor?
Obrigado.

F

wanderman, hoje estou muito atarefado, talvez não consigo acompanhar o post.
Coloque o código da sua página + faces-config para que eu possa dar uma olhada.

Abraço!

W

Opa, estou usando jsf 2 e não estou utilizando o faces-config vou colocar resumidamente os trechos do codigo então para melhor entendimento.

na minha pagina tenho a seguinte chamada ao componente que eu criei:
<util:autoComplete id="autocomplete"  valor="#{contasFace.conta}"/>
e o meu componente esta assim
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:cc="http://java.sun.com/jsf/composite"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:c="http://java.sun.com/jsp/jstl/core"
      xmlns:p="http://primefaces.prime.com.tr/ui"
      >

    <cc:interface>
	<cc:attribute name="valor" required="true"/>
	<cc:attribute name="id" required="true" />
    </cc:interface>
    <cc:implementation>
	<div id="#{cc.clientId}">	
			<p:autoComplete value="#{cc.attrs.valor}"  completeMethod="#{utilFace.obterContas}" var="objeto" 
                             itemLabel="#{objeto.descricaoConta}" itemValue="#{objeto}">
			    <f:converter converterId="entityConverter"/>
			</p:autoComplete>
	</div>
    </cc:implementation>

</html>
caso eu não use o meu componente composto e coloque o p:autocomplete direto na pagina ele consegue me trazer a classe correta ex:
<p:autoComplete value="#{contasFace.conta}"  completeMethod="#{utilFace.obterContas}" var="objeto" 
                             itemLabel="#{objeto.descricaoConta}" itemValue="#{objeto}">
			    <f:converter converterId="entityConverter"/>
			</p:autoComplete>

não sei se nessa passagem de valor par ao componente ele perde a identificação da classe ou alguma coisa parecida.

F

Para que o código funcione, sua classe tem que estender SelectOneMenu do JSF. Por exemplo, no RF 3.X, o rich:combobox não estende SelectOneMenu, desta forma, impossibilitando o uso deste converter.

Abraço

W

Então com o autocomplete do primefaces ele funciona certinho se eu estiver utilizando-o na propria pagina, o problema ocorre somente quando eu utilizo-o dentro de um componente composto, não cheguei a testar mas creio q se eu criar um componente composto utilizando o SelectOneMenu ele pode gerar o mesmo problema, mas vou tentar aqui depois te falo.
Valeu

K

CARA VOCÊ SALVOU MEU EMPPREGO E CONSEQUENTEMENTE MINHA VIDA…OBRIGADO!!!

F

Olá Kristiano, puxa vida, fico muito contente em saber que o código que compartilhei o ajudou. Se quiser melhorá-lo, vá em frente.

Abraço!

K

ow fiquei feliz demais que deu certo, so que apareceu outro problema…
ele lista os items, mas nao consigo selecionalos… alguem sabe a solução?

I

Flavio Almeida,
Cara parabéns pelo otimizada no EntityConverter, realmente ficou excelente, utilizei a versão do primeiro post, que ja resolveu o meu problema, agora irei verificar com mais calma, não cheguei a ler todo o tópico, mas tens o git-hub para que possamos contribuir? :slight_smile:

M

Parabens pelo trabalho!!! ficou bem bacana… testei aqui e funcionou perfeito.

G

Parabéns! FIcou muito bom mesmo!

Eu estava pesquisando bastante sobre converters e acabei testando essa solução e ficou muito bacana!

No fim das contas eu acabei utilizando a solução do Omnifaces http://showcase-omnifaces.rhcloud.com/showcase/converters/SelectItemsIndexConverter.xhtml. Ele não faz acesso ao banco! O único problema é colocar mais uma biblioteca na aplicação, mas se pesquisar tem um ou outra coisa válida tb no OmniFaces. To usando OmniFaces e Primefaces sem problemas!

C

Estava tentando entender este código aí… por que é necessário o método private Class<?> getClazz(FacesContext facesContext, UIComponent component) ???

Por que não podemos obter a classe direto do "obj" que vem como parâmetro no getAsString(FacesContext ctx, UIComponent component, Object obj) com obj.getClass()

Mesma coisa no component.getAttributes().put(id, getClazz(ctx, component).cast(obj));

Por que não podemos simplesmente fazer

component.getAttributes().put(id, obj); ???

F

Muito legal essa classe, vai me ajudar muito!!!

Fiz apenas um modificação, como uso a tag abaixo dentro de alguns selectOneMenus opcionais:

inclui esse teste no início do getAsString() para evitar ClassCastException:

if(obj instanceof String) { return obj.toString(); }

Valeu

L

Olá amigos, sou extremamente novato em programação, mas estudando muuuito e altas madrugadas!

Estou implementando envio de emails com picklist da biblioteca primefaces no jsf. E estou usando um converter para obter objetos do tipo Aluno, porém não estou conseguindo popular uma lista com os nomes dos alunos que estão no Banco de dados.
SEgue códigos:

//imports

@FacesConverter(value = "aluno")
@ManagedBean(name = "CommonsMail")
@SessionScoped
public class CommonsMail implements Converter {

    @EJB
    private  com.lopes.beans.AlunoFacade ejbFacade;
    private List<Aluno> nomes = new ArrayList();

//E aí tenho o metodo getAsObjetc - Meu problema

@Override
    public Object getAsObject(FacesContext facesContext, UIComponent component, String submittedValue) {
        if (submittedValue.trim().equals("")) {
            return null;
        } else {
            try {
                int number = Integer.parseInt(submittedValue);
                System.err.println("ID:" + number);

//Aqui preciso popular a List nomes com todos os nomes da tabela Aluno do BD. teoricamente a linha abaixo funcionaria, mas li em vários tópicos que não dá para usar EJB nesse método
            nomes = ejbFacade.findNomesComEmail();
                
              } catch (NumberFormatException exception) {
                throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Conversion Error", "Not a valid player"));
            }
        }
        return null;
    }

O que dá para eu fazer?? O Entity Manager também descobri que não dá para usar. Tentei fazer injeção na classe, porém não obtive sucesso!

Muito grato!

F

Pessoal, estou resgatando esse tópico pois trombei com um empecilho ao usar a entityConverter do primeiro post:

  • ao usá-la dentro de componentes que permitem múltipla seleção (selectManyCheckBox, por exemplo), é gerado um ClassCastException, pois a classe do objeto resultante da expressão value do componente é do tipo List (ou Set, ou qualquer coleção que você esteja usando no seu value), e não do tipo da entidade.

Minha solução foi passar um atributo para o componente nesses casos, e alterar o converter para que, quando o atributo estiver presente, ele pegar a classe informada no atributo, ao invés de checar o tipo do value:

<f:attribute name="classeParaEntityConverter" value="model.web.MinhaEntidade"/>

No converter:

private Class<?> getClazz(FacesContext facesContext, UIComponent component) {
		String classeParaEntityConverter = (String) component.getAttributes().get("classeParaEntityConverter");
		
		if(classeParaEntityConverter == null ) {
			return component.getValueExpression("value").getType(facesContext.getELContext());	
		} else {
			try {
				return Class.forName(classeParaEntityConverter);
			} catch (ClassNotFoundException e) {
				throw new IllegalArgumentException("A classe '" + classeParaEntityConverter + "' passada como atributo do compoente não foi encontrada");
			}
		}
	}

Alguém topou com esse problema também e talvez possa postar uma solução mais inteligente?!

Abs!

N

Alguem achou a solução para componentes que permitem múltiplas seleções???
ou alguem pode indicar postando um exemplo de converter (genérico ou não) para componentes de multiplas seleções???

K

Que iniciativa boa, parabêns.

Porém aqui não consegui implementá-lo, alguém pode me ajudar?
Possuo uma classe Contato cujo um dos atributo é do tipo empresa. Ambos anotados com @Id. O salvamento ocorre sem problemas, mas com o converter não consigo mais editar um contato.

Erro: “Error Rendering View[/views/cadastros/contato/edit.xhtml]: javax.el.PropertyNotFoundException: /views/cadastros/contato/edit.xhtml @26,150 value=”#{contatoMB.contato.empresa.id}": Target Unreachable, ‘empresa’ returned null".

Como estou usando:

<h:selectOneMenu id="contatoCodEmpresa" value="#{contatoMB.contato.empresa.id}" required="true" requiredMessage="Codigo da empresa é obrigatório">
   <f:converter converterId="entityConverter"/>		
   <f:selectItems value="#{empresaMB.listarTodos}" var="empresa" itemLabel="#{empresa.nome}" itemValue="#{empresa.id}"/>			
</h:selectOneMenu>

EmpresaMB:

...
public List<Empresa> getListarTodos(){
	return empresaService.listarTodos();
}

ContatoMB:

@ManagedBean
@RequestScoped
public class ContatoMB {

	@EJB
	private ContatoService contatoService;
	
	@EJB
	private EmpresaService empresaService;
	
	private Contato contato;
	private Empresa empresa;
	private Long empresaId;
...

web.xml

<welcome-file-list>
    <!--<welcome-file>/pages/protected/user/listAllDogs.xhtml</welcome-file>-->
    <welcome-file>/views/cadastros/contato/list.xhtml</welcome-file>
  </welcome-file-list>
  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>/faces/*</url-pattern>
    <url-pattern>*.jsf</url-pattern>
    <url-pattern>*.xhtml</url-pattern>
  </servlet-mapping>
  
      <context-param>  
      <param-name>facelets.BUILD_BEFORE_RESTORE</param-name>  
      <param-value>false</param-value>  
    </context-param>

Estou usando em um projeto no padrão JEE. Deixei o converter no modulo web e anotei no web.xml. As outras classes estão no modulo ejb.
Alguma idéia?

A

Flavio,

Esse sua EntityConverter é muito bacana e me ajudou bastante aqui. So que eu precisei fazer uma pequena alteração no seu codigo para trabalhar com @viewscoped. segue a alteração.

public Object getAsObject(FacesContext context, UIComponent component, String value) {
		if (value != null) {
			Object objeto = JsfUtil.retornarObjetoSessao(value);
			JsfUtil.removerObjetoSessao(value);
			return objeto;
		}else{
			return null;
		}
	}

	public String getAsString(FacesContext context, UIComponent component, Object objeto) {
		try {
			if (objeto != null && !objeto.equals("")) {
				String id = getId(getClasse(context, component), objeto);
				if (id == null) {
					id = "";
				}
				
				JsfUtil.adicionarObjetoSessao(id.trim(), getClasse(context, component).cast(objeto));
				return id;
			}else{
				return null;
			}
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

vlw Flavio

Criado 6 de outubro de 2010
Ultima resposta 20 de nov. de 2013
Respostas 51
Participantes 18