How to write a custom converter for

后端 未结 6 507
隐瞒了意图╮
隐瞒了意图╮ 2020-11-29 00:59

How can I write a custom converter when working with PrimeFaces components that use a list of POJO? My particular problem is with

<         


        
相关标签:
6条回答
  • 2020-11-29 01:45

    This problem is not primefaces related, just general JSF related.

    Why should you hit the database again? Your bean already contains the list of Objects you want to display in a component, or is it request scoped?

    • Create a Superclass for your Hibernate Pojo's containing an id field. If you don't want to create a super class just use the pojo class, but you need more converter classes.
    • With that Superclass you can create a generic converter for all Pojo Classes containing a list of Pojo's passed in the constructor.
    • Add the converter as a property in your session bean and use that converter in your JSF component.

    If you access the final list via a get property in your bean or the nested one in the converter is of your choice.

    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>
    
    0 讨论(0)
  • 2020-11-29 01:48

    After research on how to write custom converter, here is the solution.
    1. create a Java Class that implement 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. Register your custom converter in faces-config.xml

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

    3. So now inside Primefaces component, u just do converter="projectConverter". Note that projectConverter is the <convert-id> I just created. So to solve my problem above, I do this:

    <p:pickList converter="projectConverter" value="#{bean.projects}" var="project" 
                                itemLabel="#{project.name}" itemValue="#{project}">
    
    0 讨论(0)
  • 2020-11-29 01:53

    It's possible, whithout other database access, but i don't know the best way. I use a very specific converter, works only for picklist. Try this:

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

    and Picklist:

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

    Work's for me, improvements is necessary.

    0 讨论(0)
  • 2020-11-29 01:56

    Yes, you can write a converter that serializes/deserializes the objects in the picklist like this:

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

    Then apply this converter on your picklist like this:

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

    and make sure your objects are serializable.

    0 讨论(0)
  • 2020-11-29 01:57

    Yes, it's possible:

    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="#...
    
    0 讨论(0)
  • 2020-11-29 01:58

    is there any way to implement that without 2 database hits?

    I mean, when you have

    #{bean.projects}
    

    this is a database hit.

    and when the converter puts

    sBean.getProjectById(value);
    

    is a unnecessary database hit, since bean.projects already have id and value of the objects

    0 讨论(0)
提交回复
热议问题