2010-09-01 4 views
19

Как написать собственный конвертер при работе с компонентами PrimeFaces, которые используют список POJO? Моя проблема заключается в частности с <p:pickList>Как написать пользовательский конвертер для <p:pickList>

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

Без преобразователя я получаю java.lang.ClassCastException потому, что JSF устанавливает значения полей с необращёнными java.lang.String представленных значениями.

ответ

18

После изучения того, как написать собственный конвертер, вот решение.
1. создать класс Java, которые реализуют 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. Зарегистрировать свой собственный конвертер в faces-config.xml

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

3. Так что теперь внутри компонента Primefaces, у просто сделать converter="projectConverter". Обратите внимание, что projectConverter - это <convert-id> Я только что создал. Поэтому, чтобы решить мою проблему выше, я делаю это:

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

Есть ли способ реализовать это без 2 ударов базы данных?

Я имею в виду, когда у вас есть

#{bean.projects} 

это база хитом.

и когда преобразователь ставит

sBean.getProjectById(value); 

является ненужным хитом базы данных, так как bean.projects уже есть идентификатор и значение объектов

+0

Интересный вопрос. К сожалению, я честно не знаю. Я думаю, вы должны задать этот вопрос и посмотреть, что говорит сообщество. Если вы сделаете это в сообщение, пожалуйста, дайте мне ссылку. Я тоже хочу знать об этом. –

37

Возможно, Whithout другого доступа к базе данных, но я не» t знаю лучший способ. Я использую очень специфический конвертер, работает только для picklist. Попробуйте это:

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

и PickList:

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

Работа для меня, улучшения необходимо.

+4

Это лучшее решение. Запросить базу данных в конвертере - это самое худшее, что можно сделать, например, добавить к элементу sourcelist 1000 элементов с хотя бы одним вложенным объектом и посмотреть, что произойдет;) – Stefan

+0

+1, потому что это лучше, чем принятый ответ. именно то, что я искал. – Anas

+0

Кстати, не могли бы вы рассказать мне, что такое «Проект» в вашем коде? это двойной список в bean-компоненте? или 'var'? – Anas

5

Да, вы можете написать конвертер, который сериализующий/десериализует объекты в списке выбора, как это:

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

} 

Затем примените этот конвертер на вашем списке выбора, как это:

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

и сделать убедитесь, что ваши объекты сериализуемы.

3

Эта проблема не связана с первичными связями, а просто связана с JSF.

Зачем вам удалять базу данных еще раз? Ваш bean-компонент уже содержит список объектов, которые вы хотите отобразить в компоненте, или он запрашивает область действия?

  • Создайте суперкласс для вашего спящего режима Pojo, содержащий поле id. Если вы не хотите создавать суперкласс, просто используйте класс pojo, но вам нужно больше классов конвертеров.
  • С помощью этого Суперкласса вы можете создать общий конвертер для всех классов Pojo, содержащих список Pojo, переданный в конструкторе.
  • Добавить конвертер в качестве свойства в вашем сеансовом компоненте и использовать этот конвертер в вашем компоненте JSF.

Если вы получаете доступ к окончательному списку через свойство get в своем компоненте или вложенный в конвертор по вашему выбору.

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

Да, это возможно:

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

 Смежные вопросы

  • Нет связанных вопросов^_^