Extreme Java When vanilla Java is not enough

7Apr/100

Using f:selectItems with JPA entities

It's a common scenario: you must do a "SELECT e FROM Entity e" with JPA and pass it to a <h:selectOneMenu> or a <h:selectManyList>. Unfortunately, if you google it, you will find a lot of strange solutions.

I've tested a simple solution on JavaEE 6. On my managed bean, I can define a property like this:

...
public class MyBean {
  ...
  @PersistenceContext
  private EntityManager em;
 
  public Converter getConverter() {
    return new Converter() {
      @Override
      public Object getAsObject(
          FacesContext fc,
          UIComponent uic,
          String string) {
        return (string == null)
          ? null
          : em.find(MyEntity.class,
                    Long.parseLong(string));
      }
      @Override
      public String getAsString(
          FacesContext fc,
          UIComponent uic,
          Object o) {
        return (o == null)
          ? null
          : Long.toString(((MyEntity) o).getId());
      }
    };
  }
}

According to JSF specification, only managed beans can be injected. This is why I declared it as an inner class.

This way, the converter allows you to work only with entities, instead of manually storing IDs on the managed bean. This means the page can bind to this property when a conversion is needed:

<h:selectOneMenu value="#{myBean.myEntitySelection}">
  <f:selectItems value="#{myBean.myEntityList}"/>
  <f:converter binding="#{myBean.converter}"/>
</h:selectOneMenu>

One last step: your Entity must have an working "equals" method. If you forget this, you will receive the message "Validation Exception: Value is invalid".

Usually, a boilerplate, like this one generated by NetBeans will suffice:

@Override
public boolean equals(Object obj) {
  if (obj == null) {
    return false;
  }
  if (getClass() != obj.getClass()) {
    return false;
  }
  final MyEntity other = (MyEntity) obj;
  if (this.id != other.id
      &amp;&amp; (this.id == null
          || !this.id.equals(other.id))) {
    return false;
  }
  return true;
}

Works like a charm on JSF 2, but I haven't tested on JSF 1.2.

BTW, this solution can be refactored to became a more generic: create a base entity class that declares a "getId" method returning Serializable. Then, the converter can be a class receiving an entity manager and the persistent class.

Comments (0) Trackbacks (0)

No comments yet.


Leave a comment

(required)


*

No trackbacks yet.