selectOneMenu do not render/display value - jsf

Made a post earlier about this topic (Display values in drill-down SelectOneMenus). The problem that have is not connected to the Object converters as I first thought.
I have two selectOneMenu depending on each other, Sector -> Category. When I persist the background business object that holds a Sector and Category, everything works as expected. Even after the current user session terminates and the user log on again and edit the previous saved business object.
The problem occur when and the entity manager flushes the objects and user wants to display the Sector, Category again from the database. The Sector selectOneMenu displays its value as it should, but the Category selectOneMenu value is not displayed. Although the backing bean has the correct value from the database.
What makes the selectOneMenuto not display certain persisted values/objects when they are loaded from database instead from in-memory?
Edit.xhtml
<h:outputLabel value="Sector:" />
<h:selectOneMenu id="sectorSelector" value="#{activityController.selected.category.sector}" title="#{bundle.CreateSectorLabel_sectorName}" valueChangeListener="#{activityController.changeSectorMenu}" immediate="true">
<a4j:ajax event="change" execute="#this categoryMenu" render="categoryMenu"/>
<f:selectItems value="#{sectorController.itemsAvailableSelectOne}"/>
</h:selectOneMenu>
<h:outputLabel value="Category:" />
<h:selectOneMenu id="categoryMenu" value="#{activityController.selected.category}" title="#{bundle.CreateSectorLabel_sectorName}"
binding="#{activityController.categoryMenu}"
required="true" requiredMessage="#{bundle.CreateCategoryRequiredMessage_sector}">
<f:selectItems value="#{activityController.categorySelection}"/>
</h:selectOneMenu>
Controller bean for Category
#ManagedBean(name = "categoryController")
#SessionScoped
public class CategoryController implements Serializable{
....
#FacesConverter(forClass = Category.class)
public static class CategoryControllerConverter implements Converter {
#Override
public Object getAsObject(FacesContext facesContext, UIComponent component, String value) {
if (value == null || value.length() == 0) {
return null;
}
CategoryController controller = (CategoryController) facesContext.getApplication().getELResolver().
getValue(facesContext.getELContext(), null, "categoryController");
return controller.ejbFacade.find(getKey(value));
}
java.lang.Integer getKey(String value) {
java.lang.Integer key;
key = Integer.valueOf(value);
return key;
}
String getStringKey(java.lang.Integer value) {
StringBuffer sb = new StringBuffer();
sb.append(value);
return sb.toString();
}
#Override
public String getAsString(FacesContext facesContext, UIComponent component, Object object) {
if (object == null) {
return null;
}
if (object instanceof Category) {
Category o = (Category) object;
return getStringKey(o.getIdCategory());
}
else {
throw new IllegalArgumentException("object " + object + " is of type " + object.getClass().getName() + "; expected type: " + CategoryController.class.getName());
}
}
}
Part of POJO object
...
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idCategory")
private Integer idCategory;
...
#Override
public boolean equals(Object object) {
if (!(object instanceof Category)) {
return false;
}
Category other = (Category) object;
if ((this.idCategory == null && other.idCategory != null) || (this.idCategory != null && !this.idCategory.equals(other.idCategory))) {
return false;
}
return true;
}
Functon for creating the SelectItem[] array
public static SelectItem[] getSelectItems(List<?> entities, boolean selectOne) {
int size = entities.size();
SelectItem[] items;
int i = 0;
if (selectOne) {
items = new SelectItem[size + 1];
items[0] = new SelectItem("", ResourceBundle.getBundle("/resources/Bundle").getString("Select_item"));
i++;
} else {
items = new SelectItem[size];
}
for (Object x : entities) {
items[i++] = new SelectItem(x, x.toString());
}
return items;
}

The problem was that I didn't have the right collection of SelectItems when the user edited a business object. Plus that I added the <f:converter> to explicitly invoke the converter, where I did some changes in the getAsString() (the else part).
Thank you for your time, and hope the post can be useful to someone else.
SelectMenuOne
public String prepareEditOrganisationActivity() {
current = (Activity) organisationActivityItems.getRowData();
Sector sector = current.getCategory().getSector();
CategoryController categoryBean = JsfUtil.findBean("categoryController", CategoryController.class);
categorySelection = categoryBean.categoryItemsBySector(sector);
return "EditActivity";
}
CategoryControllerConverter
...
#Override
public String getAsString(FacesContext facesContext, UIComponent component, Object object) {
if (object == null) {
return null;
}
if (object instanceof Category) {
Category o = (Category) object;
return getStringKey(o.getIdCategory());
}
else {
Category tmp = new Category();
return getStringKey(tmp.getIdCategory());
}
}

Related

javax.el.PropertyNotFoundException: itemLabel="#{projet.nomProjet}": Property 'nomProjet' not found on type java.lang.String

I'm trying to apply a JSF converter to an Entity inside a selectOneMenu,
but the converter is not recognized, I get this warning in my xhtml file,
<<"nomProjet" cannot be resolved>>
and when I run the application I'm getting Error HTTP 500 :
itemLabel="#{projet.nomProjet}": Property 'nomProjet' not found on type java.lang.String
Here is my code:
The selectOneMenu in my view
<p:selectOneMenu id="projet" converter="projetConverter" value="# {affectation.selectedProjet}" >
<f:selectItems var="projet" itemValue="#{projet}" itemLabel="#{projet.nomProjet}" value="#{affectation.projetsAffectablesCollaborateur()}" />
</p:selectOneMenu>
The converter
#Component
#FacesConverter("projetConverter")
public class ProjetConverter implements Converter {
#Autowired
private ProjetRepository projetRepository;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isEmpty()) {
return null;
}
try {
Projet projet = projetRepository.findByIdProjet(Long.valueOf(value));
return projet;
} catch (NumberFormatException exception) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur de conversion", "ID de projet invalide"));
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) {
return "";
}
if (value instanceof Projet) {
return String.valueOf(((Projet) value).getIdProjet());
} else {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur de conversion", "Instance de projet invalide"));
}
}
}
And my Entity :
#Entity
#NamedQuery(name = "Projet.findAll", query = "SELECT p FROM Projet p")
public class Projet implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long idProjet;
private String nomProjet;
#Transient
private List<Role> listRoles = new ArrayList<Role>();
public List<Role> getListRoles() {
return listRoles;
}
public void setListRoles(List<Role> listRoles) {
this.listRoles = listRoles;
}
// bi-directional many-to-one association to AffectationProjetRole
#OneToMany(mappedBy = "projet")
private List<AffectationProjetRole> affectationProjetRoles;
public Projet() {
}
public Projet(String nomProjet) {
this.nomProjet = nomProjet;
}
public long getIdProjet() {
return this.idProjet;
}
public void setIdProjet(long idProjet) {
this.idProjet = idProjet;
}
public String getNomProjet() {
return this.nomProjet;
}
public void setNomProjet(String nomProjet) {
this.nomProjet = nomProjet;
}
public List<AffectationProjetRole> getAffectationProjetRoles() {
return this.affectationProjetRoles;
}
public void setAffectationProjetRoles(List<AffectationProjetRole> affectationProjetRoles) {
this.affectationProjetRoles = affectationProjetRoles;
}
public AffectationProjetRole addAffectationProjetRole(AffectationProjetRole affectationProjetRole) {
getAffectationProjetRoles().add(affectationProjetRole);
affectationProjetRole.setProjet(this);
return affectationProjetRole;
}
public AffectationProjetRole removeAffectationProjetRole(AffectationProjetRole affectationProjetRole) {
getAffectationProjetRoles().remove(affectationProjetRole);
affectationProjetRole.setProjet(null);
return affectationProjetRole;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (idProjet ^ (idProjet >>> 32));
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Projet other = (Projet) obj;
if (idProjet != other.idProjet)
return false;
return true;
}
}
How is this caused and how can I solve it?
itemLabel="#{projet.nomProjet}": Property 'nomProjet' not found on type java.lang.String
This error message tells that #{projet} is during runtime actually a java.lang.String. Let's look where's #{projet} is coming from.
<f:selectItems value="#{affectation.projetsAffectablesCollaborateur()}"
var="projet" itemValue="#{projet}" itemLabel="#{projet.nomProjet}" />
Thus, #{affectation.projetsAffectablesCollaborateur()} actually returned a List<String>. If this is unexpected, then beware of generic type erasure and doublecheck all unchecked casts that the generic type is not incorrectly assumed. Generally, the mistake is in the persitence layer. For example, when you mistakenly execute the query SELECT p.nomProject FROM Project p instead of SELECT p FROM Project p and then performed an unchecked cast against List<Projet> instead of List<String>.
Do note that rendering item labels doesn't involve the converter at all, so showing and blaming it is unnecessary. The converter is only used on item values.

p:orderList converter getAsObject() doesn't call Object.toString()

I've written a custom converter as follows:
#FacesConverter(value = "orderListConverter")
public class OrderListConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
Object ret = null;
if (component instanceof OrderList) {
Object list = ((OrderList) component).getValue();
ArrayList<ExampleEntity> al = (ArrayList<ExampleEntity>) list;
for (Object o : al) {
String name = "" + ((ExampleEntity) o).getName();
if (value.equals(name)) {
ret = o;
break;
}
}
}
return ret;
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value)
{
String str = "";
if (value instanceof ExampleEntity) {
str = "" + ((ExampleEntity) value).getNumber();
}
return str;
}
}
My ExampleEntity is implemented as follows:
public class ExampleEntity {
private String name;
private int number;
public ExampleEntity(String name, int number) {
this.name = name;
this.number = number;
}
#Override
public String toString() {
return "toString(): [name=" + name + ", number=" + number + "]";
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
the orderList-Component from Primefaces looks like that:
<p:orderList value="#{orderListBean.exampleList}"
var="exampleEntity" itemValue="#{exampleEntity}"
converter="orderListConverter">
<p:column style="width:25%">
#{exampleEntity.number}
</p:column>
<p:column style="width:75%;">
#{exampleEntity.name}
</p:column>
</p:orderList>
and the bean is implemented as follows:
#SessionScoped
#ManagedBean(name = "orderListBean")
public class OrderListBean {
private List<ExampleEntity> exampleList;
#PostConstruct
public void init() {
exampleList = new ArrayList<ExampleEntity>();
exampleList.add(new ExampleEntity("nameOne", 1));
exampleList.add(new ExampleEntity("nameTwo", 2));
exampleList.add(new ExampleEntity("nameThree", 3));
exampleList.add(new ExampleEntity("nameFour", 4));
exampleList.add(new ExampleEntity("nameFive", 5));
}
public List<ExampleEntity> getExampleList() {
return exampleList;
}
public void setExampleList(List<ExampleEntity> exampleList) {
this.exampleList = exampleList;
}
}
1) when debugging, the value Parameter of getAsObject() contains
the number of the ExampleEntity, but I had expected the
toString() method of ExampleEntity to be called!
2) What is the correct content for itemValue attribute?
Is it a kind of convention over configuration? Or how does the component
'know', to use the whole object, when inserting exampleEntity into itemValue
Hope everything is clear! Tanks a lot for any explanation!
Converters basically serve to transform values in 2 directions:
Server to client, when the value is rendered.
Client to server, when the value is submitted.
In your getAsString you established, that the string representation, the one which client uses, is exampleEntity's number. So that's what gets rendered to client as a value. And later, when the client submits its value, that value is number. To convert it to the object (server) representation, the getAsObject called with the number as a parameter.
The server can't possibly call getAsObject with exampleEntity.toString(), because it doesn't have the exampleEntity instance at that point, only the submitted number.
To illustrate, this should hold:
obj.equals(conv.getAsObject(ctx, comp, conv.getAsString(ctx, comp, obj)));
getAsObject and getAsString should be inversive in their input and output.
To answer your 2nd question: that depends on your needs. You could say itemValue="#{exampleEntity.number}", but that would make sense only if you're not interested in the exampleEntity itself, i.e. on submit you would get the number from the client and that's all you need for your server-side logic.
#FacesConverter(value = "EntityConverter")
public class EntityConverter implements Converter, Serializable {
private static final long serialVersionUID = 1L;
public EntityConverter() {
super();
}
#Override
public Object getAsObject(final FacesContext context, final UIComponent component, final String value) {
if (value == null) {
return null;
}
return fromSelect(component, value);
}
/**
* #param currentcomponent
* #param objectString
* #return the Object
*/
private Object fromSelect(final UIComponent currentcomponent, final String objectString) {
if (currentcomponent.getClass() == UISelectItem.class) {
final UISelectItem item = (UISelectItem) currentcomponent;
final Object value = item.getValue();
if (objectString.equals(serialize(value))) {
return value;
}
}
if (currentcomponent.getClass() == UISelectItems.class) {
final UISelectItems items = (UISelectItems) currentcomponent;
final List<Object> elements = (List<Object>) items.getValue();
for (final Object element : elements) {
if (objectString.equals(serialize(element))) {
return element;
}
}
}
if (!currentcomponent.getChildren().isEmpty()) {
for (final UIComponent component : currentcomponent.getChildren()) {
final Object result = fromSelect(component, objectString);
if (result != null) {
return result;
}
}
}
if (currentcomponent instanceof OrderList) {
Object items = ((OrderList) currentcomponent).getValue();
List<Object> elements = (List<Object>) items;
for (final Object element : elements) {
if (objectString.equals(serialize(element))) {
return element;
}
}
}
return null;
}
/**
* #param object
* #return the String
*/
private String serialize(final Object object) {
if (object == null) {
return null;
}
return object.getClass() + "#" + object.hashCode();
}
#Override
public String getAsString(final FacesContext arg0, final UIComponent arg1, final Object object) {
return serialize(object);
}
}

<f:selectItems> only shows toString() of the model as item label

I am facing a problem to populate SelectOneMenu correctly using converter. I followed several google references and lastly http://balusc.blogspot.in/2007/09/objects-in-hselectonemenu.html link of BalusC. But could not figure out the problem. I am giving all my entity, managedbean and jsf code for your consideration.
Entity:
#Entity
#Table(name = "gender")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Gender.findAll", query = "SELECT g FROM Gender g"),
#NamedQuery(name = "Gender.findById", query = "SELECT g FROM Gender g WHERE g.id = :id"),
#NamedQuery(name = "Gender.findByGender", query = "SELECT g FROM Gender g WHERE g.gender = :gender")})
public class Gender implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Column(name = "id")
private Short id;
#Size(max = 10)
#Column(name = "gender")
private String gender;
#OneToMany(mappedBy = "gender")
private List<Patient> patientList;
public Gender() {
}
public Gender(Short id) {
this.id = id;
}
public Short getId() {
return id;
}
public void setId(Short id) {
this.id = id;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
#XmlTransient
public List<Patient> getPatientList() {
return patientList;
}
public void setPatientList(List<Patient> patientList) {
this.patientList = patientList;
}
#Override
public int hashCode() {
return (id != null) ? (getClass().hashCode() + id.hashCode()) : super.hashCode();
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
return (object instanceof Gender) && (id != null) ? id.equals(((Gender) object).id) : (object == this);
}
#Override
public String toString() {
return "hms.models.Gender[ id=" + id + " ]";
}
Converter:
#FacesConverter(value = "genderConverter")
public class GenderConverter implements Converter {
#Inject GenderService gs;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if(value == null){
return null;
}
return gs.getGender(Integer.parseInt(value));
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if(value == null) {
return null;
}
if (!(value instanceof Gender)) {
throw new ConverterException("The value is not a valid Gender: " + value);
}
return ((Gender) value).getGender();
}
}
And JSF SelectOneMenu tag:
<h:selectOneMenu id="gender" value="#{registrationBean.patient.gender}" converter="genderConverter">
<f:selectItem itemValue="0" itemLabel="-- Select Gender --" />
<f:selectItems value="#{registrationBean.genderList}" />
</h:selectOneMenu>
The dropdown output is similar to "hms.models.Gender[id=1]" and so on. Image sample has given below:
Can anybody help? Thanks in advance.
Your problem is two-fold.
Firstly, in the output the select item label is being presented, not the select item value. The converter only affects the select item value, not the select item label. Unless otherwise specified via itemLabel attribute, the select item label defaults to toString() outcome of the select item value.
Assuming that you want to display the gender property as select item label, then this should do:
<f:selectItems value="#{registrationBean.genderList}" var="gender"
itemValue="#{gender}" itemLabel="#{gender.gender}" />
(it should be said that having a property with the same name as the class itself is quite awkward)
That should fix the presentation fail.
Secondly, you're for the output converting from Gender instance to its gender property. However, you're for the input attempting to interpret the outputted gender property as an id in order to find the associated Gender instance. This doesn't make any sense. Fix the converter accordingly to give the id back in getAsString(), exactly as expected by getAsObject().
return ((Gender) value).getId().toString();
That should fix the form submit fail.
See also:
How to populate options of h:selectOneMenu from database?

JSF Converter Validation Error: Value not valid

I have a rich:pickList, when submit values it still displays error: "Validation Error: Value is not valid". I also set breakpoint to debug (getAsobject), but after system invocation I had nothing.
BalusC said that I would implement the equals() method in my entities, or I have the entities in the web service, and I fill the right side of the picklist with data from this web service.
xHTML file
<h:form>
<rich:panel>
<h:panelGrid columns="2" styleClass="criteresSaisie"
rowClasses="critereLigne" columnClasses="titreColonne,">
<h:outputLabel for="libelleComplement" value=" "
size="20" />
<rich:pickList id="libelleComplement" sourceCaption="Compléments"
targetCaption="Compléments sélectionnés"
value="#{listeCmpltDispoModel.listeCmpltSelect}" size="15"
addText=">" addAllText=">>" removeText="<"
removeAllText="<<" listWidth="270px" listHeight="110px"
orderable="true">
<f:selectItems value="#{listeCmpltDispoModel.listeCmpltDispo}"
var="liste" itemLabel="#{liste.libelleComplement}"
itemValue="#{liste}" />
<f:converter converterId="cmpltsTitresConcerter" />
</rich:pickList>
</h:panelGrid>
<h:panelGroup>
<div align="right">
<h:panelGrid columns="8">
<h:commandButton value="Valider"
action="#{saisieCmpltsTitreCtrl.valider}" />
</h:panelGrid>
</div>
</h:panelGroup>
</rich:panel>
</h:form>
Converter
#FacesConverter(value="cmpltsTitresConcerter")
public class CmpltsTitresConcerter implements Converter
{
public Object getAsObject(FacesContext context, UIComponent component, String value)
{
ComplementsDispoSortieDTO cmpltSelect= new ComplementsDispoSortieDTO();
if(value != null)
{
cmpltSelect.setCdComplement(Long.parseLong(value));
//cmpltSelect.setLibelleComplement("aaa");
}
return cmpltSelect;
}
public String getAsString(FacesContext arg0, UIComponent arg1, Object obj)
{
String result = null;
if(obj != null)
{
result = String.valueOf(((ComplementsDispoSortieDTO) obj).getCdComplement());
}
return result;
}
}
Model
#ManagedBean(name = "listeCmpltDispoModel")
#SessionScoped
public class ListeCmpltDispoModel implements Serializable {
private static final long serialVersionUID = 1L;
private Long cdComplement;
private String libelleComplement;
private int nbCompl;
private List<ComplementsDispoSortieDTO> listeCmpltDispo ;
private List<ComplementsDispoSortieDTO> listeCmpltSelect ;
public ListeCmpltDispoModel() {
}
public Long getCodeComplement() {
return cdComplement;
}
public void setCodeComplement(Long cdComplement) {
this.cdComplement = cdComplement;
}
public String getLibelleComplement1() {
return libelleComplement;
}
public void setLibelleComplement1(String libelleCoplement) {
this.libelleComplement = libelleCoplement;
}
public Long getCdComplement() {
return cdComplement;
}
public void setCdComplement(Long cdComplement) {
this.cdComplement = cdComplement;
}
public String getLibelleComplement() {
return libelleComplement;
}
public void setLibelleComplement(String libelleComplement) {
this.libelleComplement = libelleComplement;
}
public List<ComplementsDispoSortieDTO> getListeCmpltDispo() {
return listeCmpltDispo;
}
public void setListeCmpltDispo(List<ComplementsDispoSortieDTO> listeCmpltDispo) {
this.listeCmpltDispo = listeCmpltDispo;
}
public int getNbCompl() {
return nbCompl;
}
public void setNbCompl(int nbCompl) {
this.nbCompl = nbCompl;
}
public List<ComplementsDispoSortieDTO> getListeCmpltSelect() {
return listeCmpltSelect;
}
public void setListeCmpltSelect(List<ComplementsDispoSortieDTO> listeCmpltSelect) {
this.listeCmpltSelect = listeCmpltSelect;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((cdComplement == null) ? 0 : cdComplement.hashCode());
result = prime
* result
+ ((libelleComplement == null) ? 0 : libelleComplement
.hashCode());
result = prime * result
+ ((listeCmpltDispo == null) ? 0 : listeCmpltDispo.hashCode());
result = prime
* result
+ ((listeCmpltSelect == null) ? 0 : listeCmpltSelect.hashCode());
result = prime * result + nbCompl;
return result;
}
#Override
public boolean equals(Object obj){
if(!(obj instanceof ComplementsDispoSortieDTO)){
return false;
}
return ((ComplementsDispoSortieDTO)obj).getCdComplement()==this.cdComplement;
}
}
The equals() method of your entity is not right. It's not only in the wrong class (the backing bean class instead of the model class), but it's also using == on an Object (the == on an object only tests the reference, not the internal value; as a starter, you should have experienced this mistake on String values already).
The == on a Long would only return true is the Long instance was created by Long#valueOf() or Long#parseLong() and the value is between -128 and 127. Anything else, including new Long(value) and those with values beyond the given "flyweight" range, return false.
As with every other Java object (such as your current one), you need equals() instead. Put it in the right class and implement it as follows:
public class ComplementsDispoSortieDTO {
private Long cdComplement;
// ...
#Override
public boolean equals(Object obj){
if (!(obj instanceof ComplementsDispoSortieDTO)){
return false;
}
return (cdComplement != null)
? cdComplement.equals(((ComplementsDispoSortieDTO) obj).cdComplement)
: (obj == this);
}
}
Note that I also added the missing reflexive obj == this. See also the javadoc for list of requirements.
See also:
Validation Error: Value is not valid
Right way to implement equals contract
I know which could be the solution.
You should store the list and selected element in properties and maintain them in the scope as long as the component is used.

JSF: No validator could be found for type when adding a converter and a validator to a p:selectOneMenu

I'm currently encountering the problem specified above when trying to add a converter and a validator to a single field. I know how these 2 works because I've used them before, but this is the first time I've tried both of them on a single field. Here are my codes:
<p:selectOneMenu id="bussProgram"
value="#{signupWizardBean.subscription.businessProgram}">
<f:converter binding="#{membershipProgramConverter}"></f:converter>
<f:validator binding="#{dropdownValidator}"></f:validator>
<f:selectItems value="#{signupWizardBean.membershipPrograms}"
var="plan" itemValue="#{plan}"
itemLabel="#{plan.description}" />
</p:selectOneMenu>
Converter
#Named
#ApplicationScoped
public class MembershipProgramConverter implements Converter {
#PersistenceContext
private transient EntityManager em;
#Inject
private Logger log;
#Override
public Object getAsObject(FacesContext ctx, UIComponent component,
String value) {
if (StringUtils.isBlank(value) || value.equals("0"))
return null;
return em.find(MembershipProgram.class, new Long(value));
}
#Override
public String getAsString(FacesContext fc, UIComponent uic, Object o) {
MembershipProgram mp = (MembershipProgram) value;
return mp.getId() != null ? String.valueOf(mp.getId()) : null;
}
}
Validator:
#Named
#ApplicationScoped
public class DropdownValidator implements Validator, Serializable {
private static final long serialVersionUID = -456545939475878299L;
#Inject
private ResourceBundle bundle;
#Inject
private FacesContext facesContext;
#Inject
private Logger log;
#Override
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
log.debug("[dropship-web] validating value={}", value);
if (value == null) {
FacesMessage msg = new FacesMessage(
bundle.getString("error.requiredField"),
bundle.getString("error.requiredField"));
msg.setSeverity(FacesMessage.SEVERITY_ERROR);
facesContext.addMessage("promoCode", msg);
throw new ValidatorException(msg);
}
}
}
Entity Class:
#Entity
#Table(name = "CRM_MEMBERSHIP_PROGRAMS", uniqueConstraints = #UniqueConstraint(columnNames = { "code" }))
#SequenceGenerator(name = "ID_GENERATOR", sequenceName = "CRM_MEMBERSHIP_PROGRAM_SEQ")
public class MembershipProgram extends BaseEntity {
private static final long serialVersionUID = -7796298586810386239L;
#Size(max = 25)
#Column(name = "CODE", nullable = false)
private String code;
#Size(max = 100)
#Column(name = "DESCRIPTION", nullable = true)
private String description;
public MembershipProgram() {
}
public MembershipProgram(long id, String description) {
setId(id);
this.description = description;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
#Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((code == null) ? 0 : code.hashCode());
result = prime * result
+ ((description == null) ? 0 : description.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
MembershipProgram other = (MembershipProgram) obj;
if (code == null) {
if (other.code != null)
return false;
} else if (!code.equals(other.code))
return false;
if (description == null) {
if (other.description != null)
return false;
} else if (!description.equals(other.description))
return false;
return true;
}
}
Note that on debug: both the MembershipProgramConverter.getAsObject/getAsString, DropdownValidator.validate methods are called.
I also found out that with or without selected item from the dropdown list, I'm encountering:
.validation.UnexpectedTypeException: HV000030: No validator could be found for type: com.czetsuya.models.membership.MembershipProgram.
javax.validation.UnexpectedTypeException: HV000030: No validator could be found for type: com.czetsuya.models.membership.MembershipProgram.
Finally I was able to figure out the problem, I've added #Size annotation on the target entity, just removed the #Size:
#Size(max = 25)
#OneToOne(optional = false)
#JoinColumn(name = "BUSINESS_PROGRAM", nullable = false)
private MembershipProgram businessProgram;
When I removed the #Size, the problem was solved but I have encountered a new one, on submit it the form always throw:
Validation Error: Value is not valid
To fixed the Value is not valid, make sure that you have properly implemented Model.equals method. My fault was that I rely on eclipse generate hashcode and equals feature, I failed to notice that it calls the equals method from my BaseEntity.

Resources