Convertisseur d’entité JSF générique

J’écris ma première application Web Java EE 6 en tant qu’exercice d’apprentissage. Je n’utilise pas de framework, mais uniquement JPA 2.0, EJB 3.1 et JSF 2.0.

J’ai un convertisseur personnalisé pour convertir une entité JPA stockée dans un composant SelectOne en une entité. J’utilise un InitialContext.lookup pour obtenir une référence à un bean session afin de trouver l’entité appropriée.

J’aimerais créer un convertisseur d’entité générique afin de ne pas avoir à créer un convertisseur par entité. Je pensais que je créerais une entité abstraite et que toutes les entités l’étendraient. Créez ensuite un convertisseur personnalisé pour l’entité abstraite et utilisez-le comme convertisseur pour toutes les entités.

Cela vous semble-t-il raisonnable et / ou réalisable?

Serait-il plus logique de ne pas avoir une entité abstraite, juste un convertisseur qui convertit n’importe quelle entité? Dans ce cas, je ne sais pas comment obtenir une référence au bean session approprié.

J’ai inclus mon convertisseur actuel car je ne suis pas sûr d’obtenir une référence à mon Session Bean de la manière la plus efficace.

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, Ssortingng 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(Ssortingng.format("Cannot obtain InitialContext - %s", e)), e); } try { return classificationEJB.getClassificationById(Long.valueOf(value)); } catch (Exception e) { throw new ConverterException(new FacesMessage(Ssortingng.format("Cannot convert %s to Classification - %s", value, e)), e); } } @Override public Ssortingng getAsSsortingng(FacesContext context, UIComponent component, Object value) { return Ssortingng.valueOf(((Classification) value).getId()); } } 

Eh bien, j’ai eu le même problème aujourd’hui et je l’ai résolu en créant un ConversionHelper générique et en l’utilisant dans le convertisseur. À cette fin, j’ai un EntityService qui est un SLSB générique que j’utilise pour effectuer des opérations CRUD simples pour tout type d’entité. De plus, mes entités implémentent une interface PersistentEntity, qui comprend les méthodes getId et setId, que je conserve avec des clés primaires simples. C’est tout.

Au final, mon convertisseur ressemble à ceci:

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

Et mon assistant de conversion ressemble à ceci:

public final class ConversionHelper { private ConversionHelper() { } public static  T getAsObject(Class returnType, Ssortingng 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 Ssortingng getAsSsortingng(Object value) { if (value instanceof PersistentEntity) { PersistentEntity result = (PersistentEntity) value; return Ssortingng.valueOf(result.getId()); } return null; } }
public final class ConversionHelper { private ConversionHelper() { } public static  T getAsObject(Class returnType, Ssortingng 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 Ssortingng getAsSsortingng(Object value) { if (value instanceof PersistentEntity) { PersistentEntity result = (PersistentEntity) value; return Ssortingng.valueOf(result.getId()); } return null; } } 

Maintenant, créer des convertisseurs pour des entités JPA simples consiste à dupliquer un convertisseur et à modifier 3 parameters.

Cela fonctionne bien pour moi, mais je ne sais pas si c’est la meilleure approche en termes de style et de performance. Des conseils seraient appréciés.

J’utilise JSF 2.0 voir la carte:

 @FacesConverter("entityConverter") public class EntityConverter implements Converter { private static final Ssortingng key = "com.example.jsf.EntityConverter"; private static final Ssortingng 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, Ssortingng value) { if (value.isEmpty()) { return null; } return getViewMap(context).get(value); } @Override public Ssortingng getAsSsortingng(FacesContext context, UIComponent c, Object value) { if (value == null) { return empty; } Ssortingng id = ((Persistent) value).getId().toSsortingng(); getViewMap(context).put(id, value); return id; } } 

ma solution est la suivante:

 @ManagedBean @SessionScoped public class EntityConverterBuilderBean { private static Logger logger = LoggerFactory.getLogger(EntityConverterBuilderBean.class); @EJB private GenericDao dao; public GenericConverter createConverter(Ssortingng entityClass) { return new GenericConverter(entityClass, dao); } } public class GenericConverter implements Converter { private Class clazz; private GenericDao dao; public GenericConverter(Ssortingng 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.Ssortingng s) { Object ret = null; if (!"".equals(s)) { Long id = new Long(s); ret = dao.findById(clazz, id); } return ret; } public Ssortingng getAsSsortingng(javax.faces.context.FacesContext facesContext, javax.faces.component.UIComponent uiComponent, java.lang.Object o) { if (o != null) { return ((SimpleEntity) o).getId() + ""; } else { return ""; } } } 

et dans les pages:

   

Utilisez Seam Faces, il fournit une classe Converter qui fait ce que vous voulez.

org.jboss.seam.faces.conversion.Converter

Bien que ce soit un projet JBoss, Seam 3 fonctionne parfaitement avec Glassfish 3.1 et plus récent.

http://seamframework.org/Seam3/FacesModule

Sur 3.1, il y a quelques dépendances supplémentaires; voir http://blog.ringerc.id.au/2011/05/using-seam-3-with-glassfish-31.html

Essayez ceci en utilisant Seam Faces from 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, Ssortingng value) { DocumentType result = null; if (value != null && !value.sortingm().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 Ssortingng getAsSsortingng(FacesContext context, UIComponent component, Object value) { Ssortingng result = null; if (value != null && value instanceof DocumentType){ DocumentType docType = (DocumentType) value; result = docType.getId(); } return result; } } 

J’utilise quelque chose comme ça:

 @Named public class EntityConverter implements Converter { @Inject private EntityManager entityManager; @Inject private ConversionService conversionService; @Override public Object getAsObject(FacesContext context, UIComponent component, Ssortingng 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 Ssortingng getAsSsortingng(FacesContext context, UIComponent component, Object value) { Object id = this.entityManager.getEntityManagerFactory().getPersistenceUnitUtil().getIdentifier(value); return this.conversionService.convert(Ssortingng.class, id); } } 

ConversionService est défini comme ceci (l’implémentation est hors de scope ici):

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

dans le modèle, utilisez .

Pour compléter la réponse de Craig Ringer, vous pouvez utiliser le générique org.jboss.seam.faces.conversion.ObjectConverter du Seam 3 FacesModule .

Vous pouvez récupérer le code ici: https://github.com/seam/faces/blob/develop/impl/src/main/java/org/jboss/seam/faces/conversion/ObjectConverter.java

Il utilise 2 HashMap (dont l’un est inversé) et stocke ses objects dans la Conversation .