Frage

Wie kann ich schreiben einen benutzerdefinierten Konverter, wenn sie mit PrimeFaces Komponenten arbeiten, die eine Liste von POJO verwenden? Mein besonderes Problem ist mit <p:pickList>

<p:pickList converter="????" value="#{bean.projects}" var="project" 
                             itemLabel="#{project.name}" itemValue="#{project}">

Ohne Konverter I java.lang.ClassCastException bekommen, weil JSF die vorgelegten Werte mit nicht umgesetzten java.lang.String vorgelegten Werte setzt.

War es hilfreich?

Lösung 2

Nach der Forschung auf, wie benutzerdefinierte Konverter zu schreiben, hier ist die Lösung.
1. eine Java-Klasse, die javax.faces.convert.Converter;

implementieren
public class ProjectConverter implements Converter{

   @EJB
   DocumentSBean sBean;

   public ProjectConverter(){
   }

   public Object getAsObject(FacesContext context, UIComponent component, String value){
     return sBean.getProjectById(value);
     //If u look below, I convert the object into a unique string, which is its id.
     //Therefore, I just need to write a method that query the object back from the 
     //database if given a id. getProjectById, is a method inside my Session Bean that
     //does what I just described
   }

   public String getAsString(FacesContext context, UIComponent component, Object value)     
   {
     return ((Project) value).getId().toString(); //--> convert to a unique string.
   }
}

2. Registrieren Sie Ihr benutzerdefinierten Konverter in faces-config.xml

<converter>
    <converter-id>projectConverter</converter-id>
    <converter-class>org.xdrawing.converter.ProjectConverter</converter-class>
</converter>

3. So, jetzt innerhalb Primefaces Komponente, u tun nur converter="projectConverter". Beachten Sie, dass projectConverter ist die <convert-id> ich gerade erstellt haben. Also mein Problem zu lösen oben, ich dies tun:

<p:pickList converter="projectConverter" value="#{bean.projects}" var="project" 
                            itemLabel="#{project.name}" itemValue="#{project}">

Andere Tipps

Es ist möglich, whithout anderer Datenbankzugriff, aber ich weiß nicht, die beste Art und Weise. Ich benutze einen sehr spezieller Konverter, funktioniert nur für Auswahlliste. Versuchen Sie folgendes:

@FacesConverter(value = "primeFacesPickListConverter")public class PrimeFacesPickListConverter implements Converter {
@Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) {
    Object ret = null;
    if (arg1 instanceof PickList) {
        Object dualList = ((PickList) arg1).getValue();
        DualListModel dl = (DualListModel) dualList;
        for (Object o : dl.getSource()) {
            String id = "" + ((Project) o).getId();
            if (arg2.equals(id)) {
                ret = o;
                break;
            }
        }
        if (ret == null)
            for (Object o : dl.getTarget()) {
                String id = "" + ((Project) o).getId();
                if (arg2.equals(id)) {
                    ret = o;
                    break;
                }
            }
    }
    return ret;
}

@Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2) {
    String str = "";
    if (arg2 instanceof Project) {
        str = "" + ((Project) arg2).getId();
    }
    return str;
}

und Auswahlliste:

<p:pickList converter="primeFacesPickListConverter" value="#{bean.projects}" var="project" 
                        itemLabel="#{project.name}" itemValue="#{project}">

Arbeit für mich, Verbesserungen notwendig.

Ja, können Sie einen Konverter schreiben, dass serialisiert / deserialisiert die Objekte in der Auswahlliste wie folgt aus:

@FacesConverter(value="PositionMetricConverter")
public class PositionMetricConverter implements Converter {

    private static final Logger log = Logger.getLogger(PositionMetricConverter.class.getName());

    @Override
    public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String value) {
        try {
            byte[] data = Base64.decodeBase64(value);
            ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));
            Object o = ois.readObject();
            ois.close();
            return o;
        } catch (Exception e) {
            log.log(Level.SEVERE, "Unable to decode PositionMetric!", e);
            return null;
        }
    }

    @Override
    public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object value) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(value);
            oos.close();

            return Base64.encodeBase64String(baos.toByteArray());
        } catch (IOException e) {
            log.log(Level.SEVERE, "Unable to encode PositionMetric!", e);
            return "";
        }
    }

}

Dann bewerben Sie diesen Konverter auf Ihrer Auswahlliste wie folgt aus:

<p:pickList converter="PositionMetricConverter" value="#{bean.positionMetrics}" 
    var="positionMetric" itemLabel="#{positionMetric.name}" itemValue="#{positionMetric}"/>

und stellen Sie sicher, dass Ihre Objekte sind serialisierbar.

Dieses Problem ist nicht primefaces verwandt, nur allgemeine JSF Zusammenhang stehen.

Warum sollten Sie die Datenbank wieder treffen? Ihre Bohne enthält bereits die Liste der Objekte, die Sie in einer Komponente angezeigt werden sollen, oder ist es scoped anfordern?

  • Erstellen Sie eine übergeordnete Klasse für Ihre Hibernate Pojo ist ein ID-Feld enthält. Wenn Sie nicht über eine Superklasse erstellen möchten nur die pojo Klasse verwenden, aber Sie mehr Konverter-Klassen benötigen.
  • Mit dieser übergeordneten Klasse eine Liste von Pojo der einen generischen Konverter für alle Pojo Klassen erstellen können, in den Konstruktor übergeben enthält.
  • Fügen Sie den Konverter als eine Eigenschaft in Ihrer Session Bean und verwenden Sie diese Wandler in Ihrer JSF-Komponente.

Wenn Sie die endgültige Liste über eine get-Eigenschaft in Ihrem Bean oder die verschachtelten eine im Konverter zuzugreifen, ist Ihre Wahl.

public class SuperPojo
{
    protected Integer id;
    //constructor & getter
}

public class PojoTest extends SuperPojo 
{
    private String label = null;    
    //constructor & getter
}

public class SuperPojoConverter<T extends SuperPojo> implements Converter
{
    private Collection<T> superPojos;

    public IdEntityConverter(Collection<T> superPojos)
    {
        this.superPojos = superPojos;
    }

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value)
    {
        //catch exceptions and empty or  null value!
        final int intValue = Integer.parseInt(value);
        for(SuperPojo superPojo : this.superPojos)
            if(superPojo.getId().intValue() == intValue)
                return superPojo;

        return null;
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value)
    {
        //catch null and instanceof
        return String.valueOf(((SuperPojo)value).getId().intValue());
    }

    public Collection<T> getSuperPojos()
    {
        return this.superPojos;
    }
}

public class Bean 
{
    private SuperPojoConverter<PojoTest> pojoTestConverter = null;

    public Bean()
    {
        final List<PojoTest> pojoTests = //get list from hibernate
        this.pojoTestConverter = new SuperPojoConverter<PojoTest>(pojoTests);
    }

    public SuperPojoConverter<PojoTest> getPojoTestConverter()
    {
        return this.pojoTestConverter;
    }
}


<h:selectOneMenu value="#{selPojoTest}" converter="#{bean.getPojoTestConverter}">
    <f:selectItems value="#{bean.getPojoTestConverter.getSuperPojos}" var="varPojoTest" itemLabel="#{varPojoTest.label}" itemValue="#{varPojoTest}"/>
</h:selectOneMenu>

Gibt es eine Möglichkeit, dass Treffer ohne 2-Datenbank zu implementieren?

Ich meine, wenn Sie

haben
#{bean.projects}

Dies ist ein Datenbank-Hit.

und wenn die Wandler puts

sBean.getProjectById(value);

ist ein unnötiger Datenbank-Hit, da bean.projects bereits id und den Wert der Objekte

Ja, es ist möglich:

public class DocumentSBean sBean implements Serializable{

private List<Document> projects;
// projects methods...
// ...

public Converter getDocumentConverter(){
 return docConverter;
}

private Converter docConverter = new Converter() {

        @Override
        public Object getAsObject(FacesContext context, UIComponent component, String value) {
            return projects.stream().filter(p -> p.getName().equals(value)).findFirst().orElse(null);
        }

        @Override
        public String getAsString(FacesContext context, UIComponent component, Object value) {
            return (value != null)
                    ? ((Document) value).toString()
                    : null;
        }
    };
}
<p:pickList converter="#{sBean.documentConverter}" value="#...
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top