2010-09-01 11 views
19

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

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

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

Antwort

18

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

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 Ihren 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 implementieren, u tun converter="projectConverter" einfach. Beachten Sie, dass projectConverter die <convert-id> ist, die ich gerade erstellt habe. So, um mein Problem oben zu lösen, tue ich das:

2

gibt es eine Möglichkeit, das ohne 2 Datenbanktreffer zu implementieren?

Ich meine, wenn Sie

#{bean.projects} 

haben dies ist ein Datenbank-Hit.

und wenn der Wandler

sBean.getProjectById(value); 
setzt

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

+0

Interessante Frage. Leider weiß ich ehrlich nicht. Ich denke, du solltest das zu einer Frage machen und sehen, was die Gemeinde sagt. Wenn Sie dies zu einem Beitrag machen, geben Sie mir bitte den Link. Ich möchte das auch wissen. –

37

Es ist möglich, whithout anderen Datenbankzugriff, aber ich don‘ Ich kenne den besten Weg. Ich benutze einen sehr spezifischen Konverter, funktioniert nur für die 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 ist.

+4

Dies ist die beste Lösung. Abfragen der Datenbank in einem Konverter ist das Schlimmste, was man tun kann, d. H. Hinzufügen von 1000 Elementen zur Quellliste mit mindestens einer verschachtelten Entität, und sehen, was passiert;) – Stefan

+0

+1, weil dies besser ist als die akzeptierte Antwort. genau das was ich gesucht habe. – Anas

+0

übrigens, können Sie mir sagen, was ist "Projekt" in Ihrem Code? ist es die Dual-Liste in Bean? oder 'var'? – Anas

5

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

@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 machen Vergewissern Sie sich, dass Ihre Objekte serialisierbar sind.

3

Dieses Problem ist nicht primafaces bezogen, nur allgemeine JSF bezogen.

Warum sollten Sie die Datenbank erneut aufrufen? Ihre Bean enthält bereits die Liste der Objekte, die in einer Komponente angezeigt werden sollen, oder ist es ein Anforderungsbereich?

  • Erstellen Sie eine Superklasse für Ihre Hibernate Pojo's mit einem ID-Feld. Wenn Sie keine Superklasse erstellen möchten, verwenden Sie einfach die Pojo-Klasse, aber Sie benötigen mehr Konverterklassen.
  • Mit dieser Superklasse können Sie einen generischen Konverter für alle Pojo-Klassen erstellen, der eine Liste von Pojos enthält, die im Konstruktor übergeben wurden.
  • Fügen Sie den Konverter als Eigenschaft in Ihrer Session-Bean hinzu und verwenden Sie diesen Konverter in Ihrer JSF-Komponente.
  • Wenn Sie die endgültige Liste über eine Get-Eigenschaft in Ihrem Bean zugreifen oder die verschachtelte im Converter Ihrer Wahl ist.

    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> 
    
    1

    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="#...