Wednesday, February 9, 2011

Hibernate, JSF 2 and ManyToMany

It can be quite a bit of a headache if you have multiple checkboxes (or similar) that are mapped to a @ManyToMany relationship using JSF 2 and Hibernate JPA.


In this case, the selectManyCheckbox is mapped to a Set which is annotated with @ManyToMany using a join table. If you want to avoid repeated LIEs when submitting the form you need to add the following attribute:

collectionType="java.util.HashSet"

to h:selectManyCheckbox.

If you use the Hibernate JPA persistence provider and choose default or lazy as the association fetch value an org.hibernate.LazyInitializationException exception can occur at runtime unless you set the collection type. This problem is apparently specific to Hibernate JPA.

14 comments:

  1. Hi Manuel,

    I am getting the same LIEs when submitting the form.
    Where ro I have to add the collectionType attribute? Is it a annotation for entity properties?

    Thanks

    ReplyDelete
  2. It's an attribute of the h:selectManyCheckbox component. See if it works for you.

    ReplyDelete
  3. Hi man, if you don`t mind about it, could you please share your configuration files as a sample? I`m struggling with that error (LIE), and doesn't matter if I set or not the "collectionType" attribute.

    Thx,
    Uilian.

    ReplyDelete
  4. That could be due to a number of other things as well. How are you mapping your associations and when are you getting the LIE?

    ReplyDelete
  5. For example, I might have all my associations mapped as EAGER (a way to avoid LIEs) but that might be a bad idea in certain cases.

    ReplyDelete
  6. Well, it`s a pretty standard user/role management, so I have in the database layer a many-to-many relationship like this, with 3 tables:

    user -> user_profile <- profile

    And I have 2 classes, User and Profile, and my mapping looks like that (just the relevant parts):

    @Entity
    @Table(name="profile")
    public class Profile implements java.io.Serializable, GrantedAuthority {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id_profile")
    private Long id;

    @Column(name = "description")
    private String description;

    @ManyToMany(fetch=FetchType.LAZY)
    @JoinTable(name = "user_profile", joinColumns = @JoinColumn(name = "id_profile"), inverseJoinColumns = @JoinColumn(name = "id_user"))
    private List users = new ArrayList();
    ... getters and setters ....

    @Entity
    @Table(name="user")
    public class User implements java.io.Serializable, UserDetails {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id_user")
    private Long id;

    @Column(name = "login")
    private String login;

    @Column(name = "password")
    private String password;

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "user_profile", joinColumns = @JoinColumn(name = "id_user"), inverseJoinColumns = @JoinColumn(name = "id_profile"))
    private List profiles = new ArrayList();
    ... getters and setters ...


    My form in view have this code, wich don`t works with lazy fetching:





    If you have any idea, please let me know.

    Thx,
    Uilian.

    ReplyDelete
  7. Sorry, my form don`t appears in the last comment because the tags where eliminated, i think. Trying using html entities:

    <h:selectManyListbox id="userProfiles"
    collectionType="java.util.ArrayList"
    value="#{userController.user.profiles}" width="145">

    <f:selectItems value="#{userController.profileList}"
    var="profile" itemLabel="#{profile.description}"
    itemValue="#{profile}" />
    </h:selectManyListbox>

    ReplyDelete
  8. The difference is that in my app I have EAGER as the fetch type. I guess you're trying to use the profiles collection while there's no session active and then you're getting the LIE. I guess you either have to use something like Seam to keep the session available during the entire conversation of load the profiles you need before closing the session.

    ReplyDelete
  9. Hi Manuel,
    I have similar setup of ManyToMany in Hibernate and selectManyCheckbox in JSF, but still strugling with putting it to work.
    I think my problem is somwhere in the converter. Could you please share code for your conceptConverter?
    Thanks in advance.
    Petr

    ReplyDelete
  10. In my converter I have:

    public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String id) {
    TimePlanDao activityDao = getWebApplicationContext(facesContext).getBean("timePlanJpaDao")
    return activityDao.findConceptById(id)
    }

    public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object object) {

    return object.toString();
    }

    ReplyDelete
  11. Thank you a lot Manuel, you made my day. I was already going crazy having disabled all related and not really related lazy loads and still catching LazyInit exception.

    ReplyDelete
  12. Thank you Manuel ! You made even my "week", a week i spent checking whether my hibernate transaction session was prematurely closed (as many other articles on the web was suggesting).

    In my case (Sun JSF 2.1.14/RichFaces 4.2.3.Final/Hibernate 4.2.0.Final/Spring 3.1.1.RELEASE) i was populating "target" and (corresponding f:selectItems' tag) "value" attributes of a rich:pickList tag using references typed as java.util.List of a given type of objects. More precisely i was getting the reference for the (f:selectItems' tag) "value" attribute from the database as some (annotated by @ManyToMany) field of some Model class. (No further configuration for @ManyTMany annotation, so fetch was by default set to FetchType.LAZY but since i was using OpenSessionInViewFilter filter, my view had access to the Hibernate session).

    The PickList loads perfectly the first time but after having some form submitted i was getting the exception:

    org.hibernate.LazyInitializationException - TRACE - failed to lazily initialize a collection, could not initialize proxy - no Session

    Simply adding the attribute collectionType="java.util.ArrayList" to my PickList solved the problem !

    ReplyDelete
  13. Muchas Gracias Manuel! You've saved my day with this post.

    ReplyDelete