Cómo llenar las opciones de h: selectOneMenu de la base de datos?

Estoy creando una aplicación web, donde debe leer una lista de objetos / entidades de un DB y en un JSF . No puedo codificar esto. ¿Puede alguien mostrarme cómo hacerlo?

Sé cómo obtener una List de la base de datos. Lo que necesito saber es cómo llenar esta lista en a .

  ...?  

Según el historial de preguntas, estás usando JSF 2.x. Entonces, aquí hay una respuesta específica de JSF 2.x. En JSF 1.x, se vería obligado a ajustar los valores de los elementos en las feas instancias de SelectItem . Afortunadamente, esto ya no es necesario en JSF 2.x.


Ejemplo básico

Para responder a su pregunta directamente, simplemente use cuyo value apunte a una propiedad de la List que conserva desde el DB durante la construcción (publicación) del bean. Aquí hay un ejemplo básico de inicio suponiendo que T realidad representa una String .

    

con

 @ManagedBean @RequestScoped public class Bean { private String name; private List names; @EJB private NameService nameService; @PostConstruct public void init() { names = nameService.list(); } // ... (getters, setters, etc) } 

Simple como eso. En realidad, el T ‘s toString() se usará para representar tanto la etiqueta como el valor del elemento desplegable. Entonces, cuando estás en lugar de List usando una lista de objetos complejos como List y no has reemplazado el método de la clase ‘ toString() , entonces com.example.SomeEntity@hashcode como item valores. Vea la siguiente sección cómo resolverlo correctamente.

También tenga en cuenta que el valor de bean for no necesariamente tiene que ser el mismo bean que el bean para el valor de . Esto es útil cuando los valores son en realidad constantes de toda la aplicación que solo tiene que cargar una vez durante el inicio de la aplicación. Entonces podría convertirlo en propiedad de un bean con ámbito de aplicación.

    

Objetos complejos como artículos disponibles

Siempre que T refiera a un objeto complejo (javabean), como User que tiene una propiedad String de name , entonces podrías usar el atributo var para obtener la variable de iteración que a su vez puedes usar en itemValue y / o itemLabel attribtues ( si omite el elemento itemLabel , la etiqueta se convierte en el mismo que el valor).

Ejemplo 1:

    

con

 private String userName; private List users; @EJB private UserService userService; @PostConstruct public void init() { users = userService.list(); } // ... (getters, setters, etc) 

O cuando tiene un id propiedad Long que le gustaría establecer como valor de artículo:

Ejemplo # 2:

    

con

 private Long userId; private List users; // ... (the same as in previous bean example) 

Objeto complejo como elemento seleccionado

Siempre que desee establecerlo en una propiedad T en el bean y T represente un User , entonces deberá hornear un Converter personalizado que convierta entre el User y una representación de cadena única (que puede ser la propiedad del id ). Tenga en cuenta que itemValue debe representar el objeto complejo en sí mismo, exactamente el tipo que debe establecerse como el value del componente de selección.

    

con

 private User user; private List users; // ... (the same as in previous bean example) 

y

 @ManagedBean @RequestScoped public class UserConverter implements Converter { @EJB private UserService userService; @Override public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) { if (submittedValue == null || submittedValue.isEmpty()) { return null; } try { return userService.find(Long.valueOf(submittedValue)); } catch (NumberFormatException e) { throw new ConverterException(new FacesMessage(String.format("%s is not a valid User ID", submittedValue)), e); } } @Override public String getAsString(FacesContext context, UIComponent component, Object modelValue) { if (modelValue == null) { return ""; } if (modelValue instanceof User) { return String.valueOf(((User) modelValue).getId()); } else { throw new ConverterException(new FacesMessage(String.format("%s is not a valid User", modelValue)), e); } } } 

(tenga en cuenta que el Converter es un poco raro para poder inyectar un @EJB en un convertidor JSF, normalmente uno lo habría anotado como @FacesConverter(forClass=User.class) , pero eso lamentablemente no permite @EJB Inyecciones de @EJB )

No se olvide de asegurarse de que la clase de objeto complejo tenga equals() y hashCode() correctamente implementados , de lo contrario JSF durante el procesamiento no podrá mostrar los elementos preseleccionados, y usted en la cara del envío Error de validación: El valor no es válido

 public class User { private Long id; @Override public boolean equals(Object other) { return (other != null && getClass() == other.getClass() && id != null) ? id.equals(((User) other).id) : (other == this); } @Override public int hashCode() { return (id != null) ? (getClass().hashCode() + id.hashCode()) : super.hashCode(); } } 

Objetos complejos con un convertidor genérico

Dirígete a esta respuesta: Implementa convertidores para entidades con Java Generics .


Objetos complejos sin un convertidor personalizado

La biblioteca de utilidades JSF OmniFaces ofrece un conversor especial de la caja que le permite usar objetos complejos en sin la necesidad de crear un convertidor personalizado. SelectItemsConverter simplemente realizará la conversión en función de los elementos fácilmente disponibles en .

    

Ver también:

  • Nuestra página wiki

Ver pagina

    

Backing-Bean

  List names = new ArrayList(); //-- Populate list from database names.add(new SelectItem(valueObject,"label")); //-- setter/getter accessor methods for list 

Para mostrar un registro seleccionado en particular, debe ser uno de los valores en la lista.

Transforma tu convertidor genérico para objetos complejos como elemento seleccionado

El Balusc da una respuesta general muy útil sobre este tema. Pero hay una alternativa que no presenta: el convertidor genérico Roll-your-own que maneja objetos complejos como el elemento seleccionado. Esto es muy complejo de hacer si quiere manejar todos los casos, pero es bastante simple para casos simples.

El siguiente código contiene un ejemplo de dicho convertidor. Funciona con el mismo espíritu que OmniFaces SelectItemsConverter mientras se ve a través de los elementos UISelectItem(s) de un componente para UISelectItem(s) contienen objetos. La diferencia es que solo maneja enlaces a colecciones simples de objetos de entidad o cadenas. No maneja grupos de elementos, colecciones de SelectItem , matrices y probablemente muchas otras cosas.

Las entidades a las que se une el componente deben implementar la interfaz IdObject . (Esto podría resolverse de otra manera, como usar toString ).

Tenga en cuenta que las entidades deben implementar equals de tal manera que dos entidades con el mismo ID se comparen por igual.

Lo único que debe hacer para usarlo es especificarlo como convertidor en el componente de selección, enlazar a una propiedad de entidad y una lista de entidades posibles:

      

Convertidor:

 /** * A converter for select components (those that have select items as children). * * It convertes the selected value string into one of its element entities, thus allowing * binding to complex objects. * * It only handles simple uses of select components, in which the value is a simple list of * entities. No ItemGroups, arrays or other kinds of values. * * Items it binds to can be strings or implementations of the {@link IdObject} interface. */ @FacesConverter("selectListConverter") public class SelectListConverter implements Converter { public static interface IdObject { public String getDisplayId(); } @Override public Object getAsObject(FacesContext context, UIComponent component, String value) { if (value == null || value.isEmpty()) { return null; } return component.getChildren().stream() .flatMap(child -> getEntriesOfItem(child)) .filter(o -> value.equals(o instanceof IdObject ? ((IdObject) o).getDisplayId() : o)) .findAny().orElse(null); } /** * Gets the values stored in a {@link UISelectItem} or a {@link UISelectItems}. * For other components returns an empty stream. */ private Stream getEntriesOfItem(UIComponent child) { if (child instanceof UISelectItem) { UISelectItem item = (UISelectItem) child; if (!item.isNoSelectionOption()) { return Stream.of(item.getValue()); } } else if (child instanceof UISelectItems) { Object value = ((UISelectItems) child).getValue(); if (value instanceof Collection) { return ((Collection) value).stream(); } else { throw new IllegalStateException("Unsupported value of UISelectItems: " + value); } } return Stream.empty(); } @Override public String getAsString(FacesContext context, UIComponent component, Object value) { if (value == null) return null; if (value instanceof String) return (String) value; if (value instanceof IdObject) return ((IdObject) value).getDisplayId(); throw new IllegalArgumentException("Unexpected value type"); } } 

Lo estoy haciendo así:

  1. Los modelos son ViewScoped

  2. convertidor:

     @Named @ViewScoped public class ViewScopedFacesConverter implements Converter, Serializable { private static final long serialVersionUID = 1L; private Map converterMap; @PostConstruct void postConstruct(){ converterMap = new HashMap<>(); } @Override public String getAsString(FacesContext context, UIComponent component, Object object) { String selectItemValue = String.valueOf( object.hashCode() ); converterMap.put( selectItemValue, object ); return selectItemValue; } @Override public Object getAsObject(FacesContext context, UIComponent component, String selectItemValue){ return converterMap.get(selectItemValue); } } 

y enlazar al componente con:

   

Si va a usar ID de entidad en lugar de hashCode, puede golpear una colisión, si tiene pocas listas en una página para diferentes entidades (clases) con la misma ID.

Llámame flojo, pero codificar un convertidor parece mucho trabajo innecesario. Estoy usando Primefaces y, sin haber usado un list box JSF2 sencillo o un menú desplegable antes, asumí (siendo flojo) que el widget podía manejar objetos complejos, es decir, pasar el objeto seleccionado como está a su getter / setter correspondiente como tal muchos otros widgets lo hacen. Me decepcionó encontrar (después de horas de scratching) que esta capacidad no existe para este tipo de widget sin un convertidor. De hecho, si suministras un setter para el objeto complejo en lugar de String, falla silenciosamente (simplemente no llama al setter, no hay excepción, no hay error JS), y pasé mucho tiempo revisando la excelente herramienta de solución de problemas de BalusC para encontrar la causa, en vano ya que ninguna de esas sugerencias se aplica. Mi conclusión: widget de listbox / menú necesita adaptación que otros widgets JSF2 no tienen. Esto parece engañoso y propenso a llevar al desarrollador desinformado como yo por un agujero de conejo.

Al final, me resistí a codificar un convertidor y encontré a través de prueba y error que si estableces el valor del widget en un objeto complejo, por ejemplo:

  

… cuando el usuario selecciona un elemento, el widget puede llamar a un setter de cadenas para ese objeto, por ejemplo, setSelectedThing(String thingString) {...} , y la cadena pasada es una cadena JSON que representa el objeto Thing. Puedo analizarlo para determinar qué objeto fue seleccionado. Esto se siente un poco como un hack, pero menos de un hack que un convertidor.