I have this Entity called \'Operation\':
@Entity
@Table(name=\"operation\")
public class Operation implements Serializable {
private static final long se
You're trying to pass a complex object around as HTTP request parameter which can be only be a String
. JSF/EL has builtin converters for primitives and its wrappers (e.g. int
, Integer
) and even enums. But for all other types you really need to write a custom converter. In this case you need to write a converter which converts between String
and Operation
. The String
is then used as option value (open page in browser, rightclick and View Source and notice the <option value>
). The Operation
is then used as model value. The String
should uniquely identify the Operation
object. You could use operation ID for this.
But in this particular case, with such a hardcoded map and a relatively simple model, I think it's easier to use an enum
instead.
public enum Operation {
DONATION("Donation"), EXCHANGE("Exchange");
private String label;
private Operation(String label) {
this.label = label;
}
public string getLabel() {
return label;
}
}
with
private Operation operation; // +getter +setter
public Operation[] getOperations() {
return Operation.values();
}
and
<h:selectOneMenu value="#{bean.operation}">
<f:selectItems value="#{bean.operations}" var="operation" itemValue="#{operation}" itemLabel="#{operation.label}" />
</h:selectOneMenu>
But if those values have actually to be retrieved from the DB and its size is undefinied, then you still really need a custom converter. You could in getAsString()
return the ID and in getAsObject()
use the operation DAO/EJB to get an Operation
by the ID.
@ManagedBean
@RequestScoped
public class OperationConverter implements Converter {
@EJB
private OperationService operationService;
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
// Convert here Operation object to String value for use in HTML.
if (!(value instanceof Operation) || ((Operation) value).getId() == null) {
return null;
}
return String.valueOf(((Operation) value).getId());
}
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
// Convert here String submitted value to Operation object.
if (value == null || !value.matches("\\d+")) {
return null;
}
Operation operation = operationService.find(Long.valueOf(value));
if (operation == null) {
throw new ConverterException(new FacesMessage("Unknown operation ID: " + value));
}
return operation;
}
}
Use it as follows:
<h:selectOneMenu ... converter="#{operationConverter}">
As to why it's a @ManagedBean
instead of @FacesConverter
, read this: Converting and validating GET request parameters.
Update as to the Validation Error: value not valid
error, this means that the equals()
method of the Operation
class is broken or missing. During validation, JSF compares the submitted value with the list of available values by Object#equals()
. If no one of the list matches with the submitted value, then you'll see this error. So, ensure that equals()
is properly implemented. Here's a basic example which compares by the DB technical identity.
public boolean equals(Object other) {
return (other instanceof Operation) && (id != null)
? id.equals(((Operation) other).id)
: (other == this);
}
Don't forget to implement hashCode()
as well:
public int hashCode() {
return (id != null)
? (getClass().hashCode() + id.hashCode())
: super.hashCode();
}