wake-up-neo.com

Generischer JSF-Entitätskonverter

Ich schreibe meine erste Java EE 6-Web-App als Lernübung. Ich verwende kein Framework, nur JPA 2.0, EJB 3.1 und JSF 2.0. 

Ich habe einen benutzerdefinierten Konverter, um eine in einer SelectOne-Komponente gespeicherte JPA-Entität zurück in eine Entität zu konvertieren. Ich verwende ein InitialContext.lookup, um einen Verweis auf ein Session-Bean zu erhalten, um die relevante Entität zu finden. 

Ich möchte einen generischen Entity Converter erstellen, sodass ich keinen Entity-Konverter erstellen muss. Ich dachte, ich würde eine abstrakte Entität erstellen und alle Entitäten erweitern lassen. Erstellen Sie dann einen benutzerdefinierten Konverter für die abstrakte Entität und verwenden Sie ihn als Konverter für alle Entitäten. 

Klingt das sinnvoll und/oder praktikabel? 

Wäre es sinnvoller, keine abstrakte Entität zu haben, nur einen Konverter, der eine Entität konvertiert? In diesem Fall bin ich nicht sicher, wie ich einen Verweis auf das entsprechende Session Bean erhalten würde.

Ich habe meinen aktuellen Konverter mitgeliefert, weil ich nicht sicher bin, ob ich auf effizienteste Weise einen Verweis auf meinen Session Bean erhalte.

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());
    }
}
26
retrodev

Nun, ich hatte heute das gleiche Problem und habe es gelöst, indem ich einen generischen ConversionHelper erstellt und im Konverter verwendet habe. Zu diesem Zweck habe ich einen EntityService, einen generischen SLSB, mit dem ich einfache CRUD-Operationen ausführen kann jeder Entitätstyp. Meine Entitäten implementieren auch eine PersistentEntity-Schnittstelle, die über eine getId- und eine setId-Methode verfügt, und ich behalte sie mit einfachen Primärschlüsseln. Das ist es.

Am Ende sieht mein Konverter so aus:


@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);
    }
}

Und mein Conversion-Helfer sieht so aus:


public final class ConversionHelper {

    private ConversionHelper() {
    }

    public static <T> T getAsObject(Class<T> 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;
    }
}

Beim Erstellen von Konvertern für einfache JPA-Entitäten müssen Sie nun einen Konverter duplizieren und 3 Parameter ändern.

Das funktioniert gut für mich, aber ich weiß nicht, ob es der beste Ansatz in Bezug auf Stil und Leistung ist. Irgendwelche Tipps wären dankbar.

7
rbento

Ich verwende die JSF 2.0-Ansichtskarte:

@FacesConverter("entityConverter")
public class EntityConverter implements Converter {

private static final String key = "com.example.jsf.EntityConverter";
private static final String empty = "";

private Map<String, Object> getViewMap(FacesContext context) {
    Map<String, Object> viewMap = context.getViewRoot().getViewMap();
    @SuppressWarnings({ "unchecked", "rawtypes" })
    Map<String, Object> idMap = (Map) viewMap.get(key);
    if (idMap == null) {
        idMap = new HashMap<String, Object>();
        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;
}
}
7
Ecmel Ercan

meine Lösung ist folgende:

@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 "";
        }
    }
}

und in seiten:

 <h:selectOneMenu id="x" value="#{controller.x}" 
      converter="#{entityConverterBuilderBean.createConverter('com.test.model.TestEntity')}">
2
alexandrubarbat

Versuchen Sie dies mit 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,
            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;
    }

}
1
ichalos

Verwenden Sie Seam Faces, bietet es eine Converter-Klasse, die das tut, was Sie wollen.

org.jboss.seam.faces.conversion.Converter

Während es sich um ein JBoss-Projekt handelt, funktioniert Seam 3 gut mit Glassfish 3.1 und höher.

http://seamframework.org/Seam3/FacesModule

In 3.1 gibt es einige zusätzliche Abhängigkeiten; siehe http://blog.ringerc.id.au/2011/05/using-seam-3-with-glassfish-31.html

1
Craig Ringer

(AKTUALISIERT FÜR JSF 2.3)

Ich verwende so etwas:

@FacesConverter(value = "entityConverter", managed = true)
public class EntityConverter implements Converter<Object> {

    @Inject
    private EntityManager entityManager;

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) {
        Class<?> entityType = component.getValueExpression("value").getType(context.getELContext());
        Class<?> idType = entityManager.getMetamodel().entity(entityType).getIdType().getJavaType();
        Converter idConverter = context.getApplication().createConverter(idType);
        Object id = idConverter.getAsObject(context, component, value);
        return entityManager.getReference(entityType, id);
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) {
        Object id = entityManager.getEntityManagerFactory().getPersistenceUnitUtil().getIdentifier(value);
        Converter idConverter = context.getApplication().createConverter(id.getClass());
        return idConverter.getAsString(context, component, id);
    }
}

Verwenden Sie in der Vorlage <f:converter binding="#{entityConverter}" />.

1
Xavier Dury

Um die Antwort von Craig Ringer abzuschließen, können Sie das generische org.jboss.seam.faces.conversion.ObjectConverter des Seam 3 FacesModule verwenden.

Sie können den Code hier herunterladen: https://github.com/seam/faces/blob/develop/impl/src/main/Java/org/jboss/seam/faces/conversion/ObjectConverter.Java

Es verwendet 2 HashMaps (eins wird umgekehrt verwendet) und speichert seine Objekte in der Conversation.

0
Anthony O.