Errore conversione valore di impostazione per ‘Convertitore null’ – Perché ho bisogno di un convertitore in JSF?

Ho problemi a capire come usare la selezione in JSF 2 con POJO / entity framework in modo efficace. Ad esempio, sto tentando di selezionare un’ quadro Warehouse tramite il menu a discesa sottostante:

     

E il bean gestito sotto:

 @Named @ViewScoped public class Bean { private Warehouse selectedWarehouse; private List availableWarehouses; // ... @PostConstruct public void init() { // ... availableWarehouses = new ArrayList(); for (Warehouse warehouse : warehouseService.listAll()) { availableWarehouses.add(new SelectItem(warehouse, warehouse.getName())); } } // ... } 

Si noti che utilizzo l’intera quadro Warehouse come valore di SelectItem .

Quando invio il modulo, questo non riesce con il seguente messaggio di facce:

Errore conversione valore di impostazione ‘com.example.Warehouse@cafebabe’ per ‘Convertitore nullo’.

Speravo che JSF potesse semplicemente impostare l’object Warehouse corretto sul mio bean gestito quando lo ho SelectItem in un object SelectItem . Avvolgere la mia entity framework all’interno di SelectItem intendeva saltare la creazione di un Converter per la mia quadro.

Devo davvero usare un Converter ogni volta che voglio fare uso di quadro nel mio ? Dovrebbe essere ansible per JSF estrarre semplicemente l’elemento selezionato dall’elenco degli articoli disponibili. Se devo davvero usare un convertitore, qual è il modo pratico di farlo? Finora mi sono avvicinato a questo:

  1. Creare un’implementazione del Converter per l’entity framework.
  2. Override di getAsString() . Penso di non averne bisogno poiché la proprietà label di SelectItem verrà utilizzata per visualizzare l’etichetta dell’opzione dropdown.
  3. Override di getAsObject() . Penso che questo sarà usato per restituire il SelectItem corretto o entity framework in base al tipo di campo selezionato definito nel bean gestito.

getAsObject() mi confonde. Qual è il modo efficace per farlo? Avendo il valore di stringa, come ottengo l’object entity framework associata? Devo interrogare l’object quadro dall’object servizio in base al valore stringa e restituire l’entity framework? O forse in qualche modo posso accedere all’elenco delle quadro che formano gli elementi di selezione, eseguirne il ciclo per trovare l’ quadro corretta e restituire l’entity framework?

Qual è l’approccio normale di questo?

    introduzione

    JSF genera HTML. HTML è in termini Java fondamentalmente una String grande. Per rappresentare oggetti Java in HTML, devono essere convertiti in String . Inoltre, quando viene inviato un modulo HTML, i valori inviati vengono trattati come String nei parametri di richiesta HTTP. Sotto le copertine, JSF li estrae da HttpServletRequest#getParameter() che restituisce String .

    Per convertire tra un object Java non standard (ovvero non una String , Number o Boolean per cui EL ha conversioni incorporate, o Date per cui JSF fornisce il incorporato ), devi fornire un Converter personalizzato. Il SelectItem non ha uno scopo speciale. È solo un avanzo da JSF 1.x quando non è stato ansible fornire ad es. List direttamente a . Non ha inoltre alcun trattamento speciale per quanto riguarda le etichette e la conversione.

    getAsString ()

    È necessario implementare il metodo getAsString() in modo tale che l’object Java desiderato sia stato rappresentato in una rappresentazione String univoca che può essere utilizzata come parametro di richiesta HTTP. Normalmente, l’ID tecnico (la chiave primaria del database) è usato qui.

     public String getAsString(FacesContext context, UIComponent component, Object modelValue) { if (modelValue == null) { return ""; } if (modelValue instanceof Warehouse) { return String.valueOf(((Warehouse) modelValue).getId()); } else { throw new ConverterException(new FacesMessage(modelValue + " is not a valid Warehouse")); } } 

    Si noti che la restituzione di una stringa vuota in caso di valore del modello null / vuoto è significativa e richiesta da javadoc . Vedi anche Utilizzo di “Please select” f: selectItem con valore null / vuoto all’interno di ap: selectOneMenu .

    getAsObject ()

    È necessario implementare getAsObject() in modo tale che esattamente la rappresentazione di String restituita da getAsString() possa essere riconvertita esattamente nello stesso object Java specificato come modelValue in getAsString() .

     public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) { if (submittedValue == null || submittedValue.isEmpty()) { return null; } try { return warehouseService.find(Long.valueOf(submittedValue)); } catch (NumberFormatException e) { throw new ConverterException(new FacesMessage(submittedValue + " is not a valid Warehouse ID"), e); } } 

    In altre parole, è necessario essere tecnicamente in grado di restituire l’object restituito come argomento modelValue di getAsString() e quindi ritrasferire la stringa ottenuta come argomento getAsObject() di getAsObject() in un ciclo infinito.

    uso

    Infine basta annotare il Converter con @FacesConverter per agganciare il tipo di object in questione, quindi JSF si occuperà automaticamente della conversione quando il tipo di Warehouse viene inserito nell’immagine:

     @FacesConverter(forClass=Warehouse.class) 

    Questo era l’approccio “canonico” del JSF. Dopo tutto non è molto efficace in quanto potrebbe anche aver appena afferrato l’object da . Ma il punto più importante di un Converter è che restituisce una rappresentazione String univoca , in modo che l’object Java possa essere identificato da una semplice String adatta per il passaggio in HTTP e HTML.

    Convertitore generico basato su toString ()

    Libreria delle utility JSF OmniFaces ha un SelectItemsConverter che funziona in base al risultato toString() dell’ quadro. In questo modo non dovrai più getAsObject() di getAsObject() e di costose operazioni di business / database. Per alcuni esempi di uso concreto, vedere anche la vetrina .

    Per usarlo, basta registrarlo come di seguito:

      

    E assicurati che toString() della tua quadro Warehouse restituisca una rappresentazione univoca dell’ quadro. Ad esempio, è ansible restituire direttamente l’ID:

     @Override public String toString() { return String.valueOf(id); } 

    O qualcosa di più leggibile / riutilizzabile:

     @Override public String toString() { return "Warehouse[id=" + id + "]"; } 

    Guarda anche:

    • Come popolare le opzioni di h: selectOneMenu dal database?
    • Convertitore di entity framework JSF generico : non è necessario scrivere un convertitore per ogni quadro.
    • Usando le enumerazioni nei selettori di JSF – le enumerazioni devono essere trattate in modo leggermente diverso
    • Come iniettare @EJB, @PersistenceContext, @Inject, @Autowired, ecc in @FacesConverter?

    Non correlato al problema, dal momento che JSF 2.0 non richiede più esplicitamente un List come valore . Basterebbe anche solo un List .

         
     private Warehouse selectedWarehouse; private List availableWarehouses; 

    Esempio di convertitore generico JSF con ABaseEntity e identificatore:

    ABaseEntity.java

     public abstract class ABaseEntity implements Serializable { private static final long serialVersionUID = 1L; public abstract Long getIdentifier(); } 

    SelectItemToEntityConverter.java

     @FacesConverter(value = "SelectItemToEntityConverter") public class SelectItemToEntityConverter implements Converter { @Override public Object getAsObject(FacesContext ctx, UIComponent comp, String value) { Object o = null; if (!(value == null || value.isEmpty())) { o = this.getSelectedItemAsEntity(comp, value); } return o; } @Override public String getAsString(FacesContext ctx, UIComponent comp, Object value) { String s = ""; if (value != null) { s = ((ABaseEntity) value).getIdentifier().toString(); } return s; } private ABaseEntity getSelectedItemAsEntity(UIComponent comp, String value) { ABaseEntity item = null; List selectItems = null; for (UIComponent uic : comp.getChildren()) { if (uic instanceof UISelectItems) { Long itemId = Long.valueOf(value); selectItems = (List) ((UISelectItems) uic).getValue(); if (itemId != null && selectItems != null && !selectItems.isEmpty()) { Predicate predicate = i -> i.getIdentifier().equals(itemId); item = selectItems.stream().filter(predicate).findFirst().orElse(null); } } } return item; } } 

    E l’uso: