I'm new to JSF and I have been trying to store data from a form that uses a h:selectOneMenu to get the category of a product. The h:selectOneMenu is being populated from the DB, however, when trying to store a product in the DB, I am getting an error: Conversion Error setting value '52' for 'null Converter'. I have reviewed similar problems in StackOverflow and tutorials online but I am still getting the error.
This is the xhtml:
<h:selectOneMenu id="category_fk" value="#{productController.product.category_fk}"
converter="#{categoryConverter}" title="Category_fk" >
<!-- DONE: update below reference to list of available items-->
<f:selectItems value="#{productController.categoryList}" var="prodCat"
itemValue="#{prodCat}" itemLabel="#{prodCat.name}"/>
</h:selectOneMenu>
This is the product controller:
@Named
@RequestScoped
public class ProductController {
@EJB
private ProductEJB productEjb;
@EJB
private CategoryEJB categoryEjb;
private Product product = new Product();
private List<Product> productList = new ArrayList<Product>();
private Category category;
private List<Category> categoryList = new ArrayList<Category>();
public String doCreateProduct()
{
product = productEjb.createProduct(product);
productList = productEjb.findAllProducts();
return "listProduct";
}
@PostConstruct
public void init()
{
categoryList = categoryEjb.findAllCategory();
productList = productEjb.findAllProducts();
}
// Getters/Setters and other methods omitted for simplicity
This is the EJB simplified for simplicity:
@Stateless
public class ProductEJB{
@PersistenceContext(unitName = "luavipuPU")
private EntityManager em;
public List<Product> findAllProducts()
{
TypedQuery<Product> query = em.createNamedQuery("findAllProducts", Product.class);
return query.getResultList();
}
public Product createProduct(Product product)
{
em.persist(product);
return product;
}
}
This is the product Entity simplified for simplicity:
@Entity
@NamedQueries({
@NamedQuery(name="findAllProducts", query = "SELECT p from Product p")
})
public class Product implements Serializable
{
private static final long serialVersionUID = 1L;
@Id @GeneratedValue(strategy= GenerationType.AUTO)
private int product_id;
private String name;
private String description;
protected byte[] imageFile;
private Float price;
@Temporal(TemporalType.TIMESTAMP)
private Date dateAdded;
@ManyToOne
private Category category_fk;
@ManyToOne
private SaleDetails saleDetails_fk;
This is the updated converter I am using:
@ManagedBean
@FacesConverter(value="categoryConverter")
public class CategoryConverter implements Converter{
@PersistenceContext
private transient EntityManager em;
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return em.find(Category.class, new Integer(value));
}
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
Category category;
category = (Category) value;
return String.valueOf(category.getCategory_id());
}
}
The code has been updated from the Orignal problem, the code perfectly works now.
You can't use a custom type on the <h:selectOneMenu/>
component (or any of the <h:selectXXXX/>
) without creating a JSF Converter. Converters exist in JSF to assist with translating essentially custom or sever-side items/constructs into web page friendly/human readable formats and also to be able to save selections from the client side back to the server side. So what the error message is essentially telling you there is that it cannot make sense enough of the value 52
submitted from the client side to save it to the server side as a Category
object/type
So what you're required to do is to create an implementation of a JSF converter
that essentially helps JSF make sense of your Category
object.
Here's a rough estimation of what you need to do:
Implement a converter for your Category
type:
// You must annotate the converter as a managed bean, if you want to inject
// anything into it, like your persistence unit for example.
@ManagedBean(name = "categoryConverterBean")
@FacesConverter(value = "categoryConverter")
public class CategoryConverter implements Converter {
@PersistenceContext(unitName = "luavipuPU")
// I include this because you will need to
// lookup your entities based on submitted values
private transient EntityManager em;
@Override
public Object getAsObject(FacesContext ctx, UIComponent component,
String value) {
// This will return the actual object representation
// of your Category using the value (in your case 52)
// returned from the client side
return em.find(Category.class, new BigInteger(value));
}
@Override
public String getAsString(FacesContext fc, UIComponent uic, Object o) {
//This will return view-friendly output for the dropdown menu
return ((Category) o).getId().toString();
}
}
Reference your new converter in your <h:selectOneMenu/>
<h:selectOneMenu id="category_fk"
converter="#{categoryConverterBean}"
value="#productController.product.category_fk}"
title="Category_fk" >
<!-- DONE: update below reference to list of available items-->
<f:selectItems value="#{productController.categoryList}"
var="prodCat" itemValue="#{prodCat.category_id}"
itemLabel="#{prodCat.name}"/>
</h:selectOneMenu>
We're using the name categoryConverterBean
because we want to take advantage of the entity manager trick you pulled in the converter. Any other case, you would've used the name you set on the converter annotation.