4 Replies Latest reply on Apr 1, 2008 4:21 PM by mcgilbertus

    selectOneMenu and JPA ManyToOne

    kenclark

      I have a lookup table and corresponding JPA component, and a table with a foreign key to that lookup table.  The JPA component for this looks like:



      @Entity
      @Name("customDataConfiguration")
      @Table(name = "custom_data_configuration")
      public class CustomDataConfiguration implements Serializable
      {
      
          @Id
          @Column(name = "custom_data_key")
          private String customDataKey;
      
          @ManyToOne
          @JoinColumn(name = "custom_data_type_code")
          private CustomDataType customDataType;
      
      // ...
      }
      



      I have a web page which allows editing of the customDataConfiguration component.  In order to allow entry of the customDataType field, I figure the easiest thing to do is make it an h:selectOneMenu, and build the SelectItem array like this:



              customDataTypes = new ArrayList<SelectItem>();
              List<CustomDataType> cdts = em.createQuery("SELECT cdt FROM CustomDataType cdt").
                      getResultList();
              for (CustomDataType cdt : cdts)
                  customDataTypes.add(new SelectItem(cdt, cdt.getCustomDataTypeDesc()));
      



      Note, the customDataTypes field is set up as:



          @Out(required = false,scope=ScopeType.SESSION)
          private List<SelectItem> customDataTypes;
      



      I wasn't really sure if this would work and, so far, it doesn't.  I get the following error:



      sourceId=selectBranchForm:typeSelection[severity=(ERROR 2), summary=(/admin/editcdconfig.xhtml @23,1
      07 value="#{customDataConfiguration.customDataType}": java.lang.IllegalArgumentException: argument t
      ype mismatch), detail=(/admin/editcdconfig.xhtml @23,107 value="#{customDataConfiguration.customData
      Type}": java.lang.IllegalArgumentException: argument type mismatch)]
      



      Is this just the wrong way to go about this or do I just have a minor error somewhere.  If there is a better solution, how does that work?


      Thanks,
      ken clark

        • 1. Re: selectOneMenu and JPA ManyToOne
          barbacena

          I don't know what the errors is, but what I do is to return the List returned by the entityManager directly and use a @Factory to cache the returned value.


          Can you post your facelets page?

          • 2. Re: selectOneMenu and JPA ManyToOne
            kenclark

            Here is the whole thing:


            <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
            "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
            <ui:composition xmlns="http://www.w3.org/1999/xhtml"
                            xmlns:s="http://jboss.com/products/seam/taglib"
                            xmlns:ui="http://java.sun.com/jsf/facelets"
                            xmlns:f="http://java.sun.com/jsf/core"
                            xmlns:h="http://java.sun.com/jsf/html"
                            xmlns:ice="http://www.icesoft.com/icefaces/component"
                            template="../layout/template.xhtml">
                
                <ui:define name="body">
                    
                    <ice:form id="selectBranchForm">
                        
                        <h:messages globalOnly="true" styleClass="message"/>
                        
                        <ice:panelGrid id="homePanelGrid" columns="1" columnClasses="leftMenu,leftMenu">
                            
                            <ice:panelGrid columns="2" columnClasses="rightMenu,leftMenu">
                                <h:outputLabel for="keyid" value="Key Id: " />
                                <h:inputText id="keyid" value="#{customDataConfiguration.customDataKey}"/>
                                <h:outputLabel for="typeSelection" value="User Entry Field Type: " />
                                <h:selectOneMenu id="typeSelection" value="#{customDataConfiguration.customDataType}">
                                    <f:selectItems value="#{customDataTypes}"/>
                                </h:selectOneMenu>
                                <h:outputLabel for="required" value="Required Entry?: " />
                                <h:selectBooleanCheckbox id="required" value="#{customDataConfiguration.required}" />                    
                                <h:outputLabel for="labelentry" value="Label: " />
                                <h:inputText id="labelentry" value="#{customDataConfiguration.customDataLabel}"/>
                                <h:outputLabel for="desc" value="Description (hint): " />
                                <h:inputTextarea id="desc" rows="4" cols="80" value="#{customDataConfiguration.customDataDesc}"/>
                            </ice:panelGrid>
                            
                            <ice:panelGrid columns="2" columnClasses="leftMenu,leftMenu">
                                <h:commandButton value="Save" action="#{customDataAction.save}" 
                                                 styleClass="button" />
                                <h:commandButton value="Cancel" action="#{customDataAction.cancel}" 
                                                 styleClass="button" />
                            </ice:panelGrid>
                            
                        </ice:panelGrid>
                        
                    </ice:form>
                    
                </ui:define> 
                </ui:composition>



            I don't believe I can just convert customDataTypes to be a List of CustomDataType objects with the f:selectItems as it is now.  How would I have to change this?


            I forgot to mention that before, the screen did display correctly -- the problem was with save.


            Thanks,
            ken

            • 3. Re: selectOneMenu and JPA ManyToOne
              sleroux

              I'm not sure, but I think you need one of the following:



              • a custom converter to convert from String to CustomDataType

              • a setCustomDataType(String s) method that accept a String and convert it, then store the value to your customDataType field (possibly annotating this method with @In)



              Hope this helps a little...

              Sylvain

              • 4. Re: selectOneMenu and JPA ManyToOne
                mcgilbertus

                I think you need a converter String - CustomDataType, and also you need to use s:selectItems tag if your data is in a List; as I understand it, f:selectItems work with a Map. I have made a page with both cases like this:



                ...
                
                <h:selectOneMenu value="#{alumnoActual.tipoDoc}">
                
                  <f:selectItems value="#{manager4.getTiposDoc()}" />
                
                </h:selectOneMenu>
                
                ...
                
                
                <h:selectOneMenu value="#{alumnoActual.localidad}" 
                
                    converter="#{localidadesList.converter}">
                
                  <s:selectItems value="#{localidades}" noSelectionLabel="----"
                
                    var="loc" label="#{loc.nombre}" />
                
                </h:selectOneMenu>
                
                ...
                
                



                baked by EJB session beans:



                @Stateful
                
                @Name("manager4")
                
                @Scope(ScopeType.SESSION)
                
                public class AlumnosMgr4 implements IAlumnosMgr4 {
                
                ...
                
                  public Map<String,String> getTiposDoc()
                
                  {
                
                    HashMap<String,String> tiposDoc = new 
                
                        HashMap<String,String>();
                
                    for (TiposDoc t : TiposDoc.values())
                
                    {
                
                      tiposDoc.put(t.getDescripcion(),t.name());
                
                    }
                
                    return tiposDoc;
                
                  }
                
                
                
                @Stateful
                
                @Name("localidadesList")
                
                @Scope(ScopeType.APPLICATION)
                
                public class LocalidadesList implements ILocalidadesList {
                
                
                  @Out
                
                  private List<Localidad> localidades;
                
                
                  @PersistenceContext
                
                  EntityManager em;
                
                
                  @Create
                
                  public void cargarLocalidades() {
                
                    localidades = em.createQuery("select l from Localidad l")
                
                        .getResultList();
                
                  }
                
                
                  public Converter getConverter() {
                
                    return new LocalidadConverter(localidades);
                
                  }
                
                
                  @Remove
                
                  @Destroy
                
                  public void destroy() {
                
                  }
                
                
                  public List<Localidad> getLocalidades() {
                
                    return localidades;
                
                  }
                
                }
                
                



                the converter:


                
                public class LocalidadConverter implements Converter {
                
                
                  private List<Localidad> localidades;
                
                  
                
                  public LocalidadConverter(List<Localidad> lista)
                
                  {
                
                    localidades = lista;
                
                  }
                
                  
                
                  public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) {
                
                    if (arg2==null || arg2.length()==0)
                
                      return null;
                
                    long id = Long.valueOf(arg2).longValue();
                
                    System.out.println("valor de id: "+arg2);
                
                    if (id<0) return null;
                
                    Localidad lRet = null;
                
                    for (Localidad l : localidades)
                
                    {
                
                      if (l.getIdlocalidad()==id)
                
                      {
                
                        lRet = l;
                
                        break;
                
                      }
                
                    }
                
                    return lRet;    
                
                  }
                
                
                  public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2) {
                
                    if (arg2==null) return "-1";
                
                    Localidad l = (Localidad)arg2;
                
                    System.out.println("Localidad: "+l.getNombre());
                
                    return String.valueOf(l.getIdlocalidad());
                
                  }
                
                
                }
                
                



                hope this helps


                Ernesto Cullen