Pregunta

¿Cómo puedo escribir un convertidor de costumbre cuando se trabaja con componentes PrimeFaces que utilizan una lista de POJO? Mi problema particular es con <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 los valores presentados java.lang.String sin convertir.

¿Fue útil?

Solución 2

Después de una investigación sobre cómo escribir convertidor de costumbre, 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. Registre su costumbre convertidor en faces-config.xml

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

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

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

Otros consejos

Es posible, whithout otro acceso de base de datos, pero no sé la mejor manera. Yo uso un convertidor muy específico, sólo funciona para 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:

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

El trabajo es para mí, mejoras es necesario.

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, se aplica este convertidor en su lista de expedición de esta manera:

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

y asegúrese de que sus objetos son serializable.

Este problema no está relacionado PrimeFaces, JSF acaba general relacionada.

¿Por qué debe golpear la base de datos de nuevo? Bean ya contiene la lista de objetos que desea mostrar en un componente, o se trata de un ámbito de petición?

  • Crear una superclase para su hibernación de Pojo que contiene un campo id. Si no desea crear un super clase sólo tiene que utilizar la clase POJO, pero se necesita más clases de convertidor.
  • Con eso superclase puede crear un conversor genérico para todas las clases POJO que contienen una lista de Pojo del pasado en el constructor.
  • Añadir el convertidor como una propiedad en su bean de sesión y el uso que en su convertidor de componentes JSF.

Si accede a la lista final a través de una propiedad se interponen en su frijol o el anidado 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>

¿hay alguna manera de poner en práctica sin que 2 golpes de base de datos?

Es decir, cuando se tiene

#{bean.projects}

Este es un golpe de base de datos.

y cuando los pone convertidor

sBean.getProjectById(value);

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

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="#...
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top