How to set @ManyToOne annotated field from SelectOneMenu?

200 Views Asked by At

I have a test .xhtml layout file

. . .
<h:form rendered="#{departmentBean.editEmployee}">
  Employee name
  <h:inputText id="fullName" value="#{departmentBean.employee.fullName}" />
  <h:selectOneMenu label="Department" value="#{departmentBean.employee.department}" converter="departmentConverter">
    <f:selectItems value="#{departmentBean.departmentList}" var="department" 
                   itemLabel="#{department.name}" itemValue="#{department}" />
  </h:selectOneMenu>
  <h:commandButton value="Save" action="#{departmentBean.save()}" />
</h:form>
. . .

Also, I have two Entity classess: Employee and Department:

@Entity
@Table(name = "departments")
public class Department implements Serializable {

  @Id
  @Column(name = "dep_pcode")
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private int id;

  @Column(name = "dep_name", nullable = false, length = 50)
  private String name;

  //. . . setters and getters
}

and

@Entity
@Table(name = "employee")
public class Employee implements Serializable {

  @Id
  @Column(name = "emp_pcode")
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private int id;

  @Column(name = "emp_fullname", nullable = false, length = 128)
  private String fullName;

  @ManyToOne
  @JoinColumn(name = "emp_depcode")
  private Department department;

  // . . . Setters and getters
}

So I need to set department in some concrete Employee via web form. To convert input value from SelectOneMenu component and vice versa, I use custom converter:

@FacesConverter("departmentConverter")
public class DepartmentConverter implements Converter {

  @EJB
  DepartmentEJB ejb;

  @Override
  public Object getAsObject(FacesContext context, UIComponent component, String value) {
    Department result = null;
    try {
        int id = Integer.parseInt(value);
        result = ejb.get(id);
    } catch (Exception e) {
      throw new ConverterException("Can't convert into Department string value: " + value);
    }
    return result;
  }

  @Override
  public String getAsString(FacesContext context, UIComponent component, Object value) {
    if (value instanceof Department) {
      return ((Department) value).getId() + "";
    }
    throw new ConverterException("Can't use departmentConverter for " + value.getClass().getName());
  }
}

But, when the form is submitted, I see validation error message.

"Department: Validation Error: Value is not valid".

I wrote custom validator for Department class

@FacesValidator("departmentValidator")
public class DepartmentValidator implements Validator {

  @Override
  public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
    if ((value instanceof Department)) {
      Department dep = (Department)value;
      if (dep.getId() == 0) {
        FacesMessage msg = new FacesMessage("Department's id = 0!",
                "Department validation failed.");
        msg.setSeverity(FacesMessage.SEVERITY_ERROR);
        throw new ValidatorException(msg);
      }
    } else {
        FacesMessage msg = new FacesMessage("Can't validate " + value.getClass().getName() + " as Department ",
                "Department validation failed.");
        msg.setSeverity(FacesMessage.SEVERITY_ERROR);
        throw new ValidatorException(msg);
    }
  }
}

And I am sure that validation value id instance of Department. But I still get validation error, that says that value is not valid.

Can I fing somewhere detailed example of how to set values for souch fields properly? What I have missed?

Thanks for your answers and wasting your time for me. Best regards.

1

There are 1 best solutions below

0
On

As temporary solution I use folowing:

  1. make @Transient field (departmentCode) in the Employee class;
  2. make @PostLoad method, which reads id of linked Department instance into departmentCode;
  3. in the .xhtml form, set value of departmentCode instead of department;
  4. in the save() method of Managed Bean, fetch Department instance by the departmentCode value;
  5. set fetched Department instance to Employee's department property;

So, Anyone know more simple way?