2010-09-01 19 views
19

¿Cómo puedo escribir un convertidor personalizado cuando trabajo con componentes PrimeFaces que usan una lista de POJO? Mi problema particular es con <p:pickList>Cómo escribir un convertidor personalizado para <p:pickList>

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

sin un convertidor consigo java.lang.ClassCastException porque JSF establece los valores presentados con java.lang.String sin convertir los valores presentados.

Respuesta

18

Después de la investigación sobre cómo escribir un convertidor personalizado, aquí está la solución.
1. crear una clase Java que implementan 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. Registra tu convertidor de costumbre en faces-config.xml

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

3. Así que ahora dentro componente Primefaces, u apenas no converter="projectConverter". Tenga en cuenta que projectConverter es el <convert-id> que acabo de crear. Entonces, para resolver mi problema anterior, hago esto:

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

¿Hay alguna forma de implementar eso sin 2 aciertos en la base de datos?

Es decir, cuando se tiene

#{bean.projects} 

esto es un golpe de base de datos.

y cuando el convertidor pone

sBean.getProjectById(value); 

es una base de datos golpe innecesario, ya que ya tienen bean.projects ID y el valor de los objetos

+0

pregunta interesante. Desafortunadamente, honestamente, no lo sé. Creo que deberías hacer una pregunta y ver qué dice la comunidad. Si haces esto en una publicación, por favor dame el enlace. Yo también quiero saber sobre eso. –

37

Es posible, whithout otro acceso de base de datos, pero i don' Conozco la mejor manera. Utilizo un convertidor muy específico, funciona solo para la lista de selección. Prueba esto:

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

y lista de selección:, mejoras es necesario

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

de trabajo para mí.

+4

Esta es la mejor solución. Consultar la base de datos en un convertidor es lo peor que se puede hacer, es decir, agregar 1000 elementos a la fuente, con al menos una entidad anidada, y ver qué ocurre;) – Stefan

+0

+1 porque es mejor que la respuesta aceptada. exactamente lo que he estado buscando. – Anas

+0

por cierto, ¿puedes decirme qué es 'Project' en tu código? ¿Es la lista dual en Bean? o 'var'? – Anas

5

Sí, se puede escribir un convertidor que serializa/deserializa los objetos en la lista de selección como esta:

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

} 

A continuación, aplicar este convertidor en su lista de selección como esta:

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

y crea Seguro que tus objetos son serializables.

3

Este problema no está relacionado con la prima, solo está relacionado con JSF.

¿Por qué debería volver a la base de datos? Su bean ya contiene la lista de Objetos que desea mostrar en un componente, o es un ámbito de solicitud?

  • Crear una superclase para su hibernación de Pojo que contiene un campo id. Si no quieres crear una súper clase simplemente utiliza la clase pojo, pero necesitas más clases de conversión.
  • Con esa Superclase, puede crear un convertidor genérico para todas las Clases de Pojo que contengan una lista de los Pojo pasados ​​en el constructor.
  • Agregue el convertidor como una propiedad en su bean de sesión y use ese convertidor en su componente JSF.

Si accede a la lista final a través de una propiedad get en su bean o la anidada en el convertidor es de su elección.

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

sí, es posible:

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