I have this Entity called ‘Operation’:
@Entity
@Table(name="operation")
public class Operation implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE)
private Integer id;
@NotNull(message="informe um tipo de operação")
private String operation;
//bi-directional many-to-one association to Product
@OneToMany(mappedBy="operation")
private List<Product> products;
// getter and setters
}
I retrieve the operations this way: (It could be through an EJB instance but just to keep it local and as an example, okay? 😉 )
public Map<String, Object> getOperations() {
operations = new LinkedHashMap<String, Object>();
operations.put("Select an operation", new Operation());
operations.put("Donation", new Operation(new Integer(1), "donation"));
operations.put("Exchange", new Operation(new Integer(2), "exchange"));
return operations;
}
So I’m trying to get the selected operation in this selectOneMenu:
The productc is a ManagedBean which has a viewScope, productb is a ManagedBean with a sessionScope which has a product which is my entity. The product contais one operation, so is something like this:
(the letter c has the meaning of control, where all operations related about my entity product should be handled by this bean, okay?)
Product productc (ViewScope)
-- ProductBean productb (SessionScope)
---- Product product (Entity)
-------- Operation operation (Entity)
The converter is the same as @BalusC is suggest before:
@ManagedBean
@RequestScoped
public class OperationConverter implements Converter {
@EJB
private EaoOperation operationService;
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
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) {
if (value == null || !value.matches("\\d+")) {
return null;
}
Operation operation = operationService.find(Integer.valueOf(value));
System.out.println("Getting the operation value = " + operation.getOperation() );
if (operation == null) {
throw new ConverterException(new FacesMessage("Unknown operation ID: " + value));
}
return operation;
}
Which retrieve the operation selected as showed in the log:
FINE: SELECT ID, OPERATION FROM operation WHERE (ID = ?)
bind => [1 parameter bound]
INFO: Getting the operation value = exchange
So when I try to submit the form gives the follow error:
form_add_product:operation: Validation error: the value is not valid
Why is this happening?
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 betweenStringandOperation. TheStringis then used as option value (open page in browser, rightclick and View Source and notice the<option value>). TheOperationis then used as model value. TheStringshould uniquely identify theOperationobject. 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
enuminstead.with
and
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 ingetAsObject()use the operation DAO/EJB to get anOperationby the ID.Use it as follows:
As to why it’s a
@ManagedBeaninstead of@FacesConverter, read this: Converting and validating GET request parameters.Update as to the
Validation Error: value not validerror, this means that theequals()method of theOperationclass is broken or missing. During validation, JSF compares the submitted value with the list of available values byObject#equals(). If no one of the list matches with the submitted value, then you’ll see this error. So, ensure thatequals()is properly implemented. Here’s a basic example which compares by the DB technical identity.Don’t forget to implement
hashCode()as well: