Conversor de entidad JSF genérico

Estoy escribiendo mi primera aplicación web Java EE 6 como ejercicio de aprendizaje. No estoy usando un framework, solo JPA 2.0, EJB 3.1 y JSF 2.0.

Tengo un Convertidor personalizado para convertir una entidad JPA almacenada en un componente SelectOne a una Entidad. Estoy usando InitialContext.lookup para obtener una referencia a Session Bean para encontrar la entidad relevante.

Me gustaría crear un convertidor de entidades genérico para no tener que crear un convertidor por entidad. Pensé que crearía una entidad abstracta y que todas las entidades la ampliarían. Luego, cree un convertidor personalizado para la entidad abstracta y úselo como convertidor para todas las entidades.

¿Suena sensato y / o practicable?

¿Tendría más sentido no tener una entidad abstracta, solo un convertidor que convierta cualquier entidad? En ese caso, no estoy seguro de cómo obtendría una referencia al Session Bean apropiado.

He incluido mi convertidor actual porque no estoy seguro de obtener una referencia a mi Session Bean de la manera más eficiente.

package com.mycom.rentalstore.converters; import com.mycom.rentalstore.ejbs.ClassificationEJB; import com.mycom.rentalstore.entities.Classification; import javax.faces.application.FacesMessage; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.convert.Converter; import javax.faces.convert.ConverterException; import javax.faces.convert.FacesConverter; import javax.naming.InitialContext; import javax.naming.NamingException; @FacesConverter(forClass = Classification.class) public class ClassificationConverter implements Converter { private InitialContext ic; private ClassificationEJB classificationEJB; @Override public Object getAsObject(FacesContext context, UIComponent component, String value) { try { ic = new InitialContext(); classificationEJB = (ClassificationEJB) ic.lookup("java:global/com.mycom.rentalstore_RentalStore_war_1.0-SNAPSHOT/ClassificationEJB"); } catch (NamingException e) { throw new ConverterException(new FacesMessage(String.format("Cannot obtain InitialContext - %s", e)), e); } try { return classificationEJB.getClassificationById(Long.valueOf(value)); } catch (Exception e) { throw new ConverterException(new FacesMessage(String.format("Cannot convert %s to Classification - %s", value, e)), e); } } @Override public String getAsString(FacesContext context, UIComponent component, Object value) { return String.valueOf(((Classification) value).getId()); } } 

Bueno, hoy tuve el mismo problema, y ​​lo resolví creando un ConversionHelper genérico y usándolo en el convertidor. Para este propósito, tengo un EntityService que es un SLSB genérico que utilizo para realizar operaciones CRUD simples para cualquier tipo de entidad. Además, mis entidades implementan una interfaz PersistentEntity, que tiene los métodos getId y setId y yo los guardo con claves primarias simples. Eso es.

Al final, mi convertidor se ve así:

@FacesConverter(value = "userConverter", forClass = User.class) public class UserConverter implements Converter { @Override public Object getAsObject(FacesContext ctx, UIComponent component, java.lang.String value) { return ConversionHelper.getAsObject(User.class, value); } @Override public String getAsString(FacesContext ctx, UIComponent component, Object value) { return ConversionHelper.getAsString(value); } }
@FacesConverter(value = "userConverter", forClass = User.class) public class UserConverter implements Converter { @Override public Object getAsObject(FacesContext ctx, UIComponent component, java.lang.String value) { return ConversionHelper.getAsObject(User.class, value); } @Override public String getAsString(FacesContext ctx, UIComponent component, Object value) { return ConversionHelper.getAsString(value); } } 

Y mi asistente de conversión se ve así:

public final class ConversionHelper { private ConversionHelper() { } public static  T getAsObject(Class returnType, String value) { if (returnType== null) { throw new NullPointerException("Trying to getAsObject with a null return type."); } if (value == null) { throw new NullPointerException("Trying to getAsObject with a null value."); } Long id = null; try { id = Long.parseLong(value); } catch (NumberFormatException e) { throw new ConverterException("Trying to getAsObject with a wrong id format."); } try { Context initialContext = new InitialContext(); EntityService entityService = (EntityService) initialContext.lookup("java:global/myapp/EntityService"); T result = (T) entityService.find(returnType, id); return result; } catch (NamingException e) { throw new ConverterException("EntityService not found."); } } public static String getAsString(Object value) { if (value instanceof PersistentEntity) { PersistentEntity result = (PersistentEntity) value; return String.valueOf(result.getId()); } return null; } }
public final class ConversionHelper { private ConversionHelper() { } public static  T getAsObject(Class returnType, String value) { if (returnType== null) { throw new NullPointerException("Trying to getAsObject with a null return type."); } if (value == null) { throw new NullPointerException("Trying to getAsObject with a null value."); } Long id = null; try { id = Long.parseLong(value); } catch (NumberFormatException e) { throw new ConverterException("Trying to getAsObject with a wrong id format."); } try { Context initialContext = new InitialContext(); EntityService entityService = (EntityService) initialContext.lookup("java:global/myapp/EntityService"); T result = (T) entityService.find(returnType, id); return result; } catch (NamingException e) { throw new ConverterException("EntityService not found."); } } public static String getAsString(Object value) { if (value instanceof PersistentEntity) { PersistentEntity result = (PersistentEntity) value; return String.valueOf(result.getId()); } return null; } } 

Ahora crear convertidores para entidades JPA simples es una cuestión de duplicar un convertidor y cambiar 3 parámetros.

Esto está funcionando bien para mí, pero no sé si es el mejor enfoque en términos de estilo y rendimiento. Cualquier consejo sería apreciado.

Estoy usando el mapa de vista JSF 2.0:

 @FacesConverter("entityConverter") public class EntityConverter implements Converter { private static final String key = "com.example.jsf.EntityConverter"; private static final String empty = ""; private Map getViewMap(FacesContext context) { Map viewMap = context.getViewRoot().getViewMap(); @SuppressWarnings({ "unchecked", "rawtypes" }) Map idMap = (Map) viewMap.get(key); if (idMap == null) { idMap = new HashMap(); viewMap.put(key, idMap); } return idMap; } @Override public Object getAsObject(FacesContext context, UIComponent c, String value) { if (value.isEmpty()) { return null; } return getViewMap(context).get(value); } @Override public String getAsString(FacesContext context, UIComponent c, Object value) { if (value == null) { return empty; } String id = ((Persistent) value).getId().toString(); getViewMap(context).put(id, value); return id; } } 

mi solución es la siguiente:

 @ManagedBean @SessionScoped public class EntityConverterBuilderBean { private static Logger logger = LoggerFactory.getLogger(EntityConverterBuilderBean.class); @EJB private GenericDao dao; public GenericConverter createConverter(String entityClass) { return new GenericConverter(entityClass, dao); } } public class GenericConverter implements Converter { private Class clazz; private GenericDao dao; public GenericConverter(String clazz, Generic dao) { try { this.clazz = Class.forName(clazz); this.dao = dao; } catch (Exception e) { logger.error("cannot get class: " + clazz, e); throw new RuntimeException(e); } } public Object getAsObject(javax.faces.context.FacesContext facesContext, javax.faces.component.UIComponent uiComponent, java.lang.String s) { Object ret = null; if (!"".equals(s)) { Long id = new Long(s); ret = dao.findById(clazz, id); } return ret; } public String getAsString(javax.faces.context.FacesContext facesContext, javax.faces.component.UIComponent uiComponent, java.lang.Object o) { if (o != null) { return ((SimpleEntity) o).getId() + ""; } else { return ""; } } } 

y en páginas:

   

Use Seam Faces, proporciona una clase de convertidor que hace lo que quiere.

org.jboss.seam.faces.conversion.Converter

Si bien es un proyecto de JBoss, Seam 3 funciona bien con Glassfish 3.1 y versiones posteriores.

http://seamframework.org/Seam3/FacesModule

En 3.1 tiene un par de dependencias adicionales; ver http://blog.ringerc.id.au/2011/05/using-seam-3-with-glassfish-31.html

Pruebe esto usando Seam Faces de Seam 3.

 @Named("DocTypeConverter") public class DocumentTypeConverter implements Converter, Serializable { private static final long serialVersionUID = 1L; @Inject private DocumentTypeSessionEJB proDocTypeSb; @Override public Object getAsObject(FacesContext context, UIComponent component, String value) { DocumentType result = null; if (value != null && !value.trim().equals("")) { try { result = (DocumentType) proDocTypeSb.findById(DocumentType.class, value); } catch(Exception exception) { throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Conversion Error", "Not a valid value")); } } return result; } @Override public String getAsString(FacesContext context, UIComponent component, Object value) { String result = null; if (value != null && value instanceof DocumentType){ DocumentType docType = (DocumentType) value; result = docType.getId(); } return result; } } 

Estoy usando algo como esto:

 @Named public class EntityConverter implements Converter { @Inject private EntityManager entityManager; @Inject private ConversionService conversionService; @Override public Object getAsObject(FacesContext context, UIComponent component, String value) { Class entityType = component.getValueExpression("value").getType(context.getELContext()); Class idType = this.entityManager.getMetamodel().entity(entityType).getIdType().getJavaType(); Object id = this.conversionService.convert(idType, value); return this.entityManager.getReference(entityType, id); // find() is possible too } @Override public String getAsString(FacesContext context, UIComponent component, Object value) { Object id = this.entityManager.getEntityManagerFactory().getPersistenceUnitUtil().getIdentifier(value); return this.conversionService.convert(String.class, id); } } 

ConversionService se define así (la implementación está fuera del scope aquí):

 public interface ConversionService {  T convert(Class targetType, Object source); } 

en la plantilla, use .

Para completar la respuesta de Craig Ringer, puede usar el genérico org.jboss.seam.faces.conversion.ObjectConverter del Seam 3 FacesModule .

Puede obtener el código aquí: https://github.com/seam/faces/blob/develop/impl/src/main/java/org/jboss/seam/faces/conversion/ObjectConverter.java

Utiliza 2 HashMap s (uno se usa de forma inversa) y almacena sus objetos en la Conversation .