1 Reply Latest reply on Jun 21, 2010 4:07 AM by Andreas Haumer

    Custom converter, DI, PC, NonUniqueObjectException

    Andreas Haumer Newbie

      Hi!


      It is a sad fact that a custom converter can not make use of DI: Seam Interceptors must be bypassed,
      EJB DI does not work, either. So there are only two ways to get a handle to a session bean like a DAO
      SLSB inside a converter: do a JNDI lookup or use Seam Component.getInstance().


      In the past couple of days I have played with custom converters in a Seam test application and found
      the following problem with this scenario: A DAO retrieved by JNDI lookup or Component.getInstance()
      has a different persistence context than the Seam action bean itself. So any entity converted by the converter
      (e.g. an entity selected in a h:selectOneMenu) will end as detached object in the action bean.


      Now when flushing the PC I get a NonUniqueObjectException if the following is true:



      1. The detached entity is associated with some parent entity, making use of transitive persistence as described in Java Persistence with Hibernate, chapter 12.1

      2. In the action bean there is already a copy of the detached entity attached to the PC



      Sadly, this is a common scenario. I often have lists of entities to be selected from in the UI
      stored in the action bean, and these entities fulfill condition (2).
      Think of a person manager which allows adding one out of several roles to a person. The person
      is an entity, so are the available roles. Both person and available roles are stored inside the
      action bean. The UI allows the user to select one role from the list, which then is associated
      with the person.


      I tried various mapping options (cascade) in combination with the Hibernate saveOrUpdate() and merge()
      operations, but I always got the same
      problem. I tested this with JBoss AS 5.1 and Seam 2.1.2. I also have to mention that I can't use
      seam managed persistence context (SMPC), as all our DAO beans also have to be usable in a non-seam
      scenario (like a remote client or other clients not running inside a seam context). So I have to
      rely on container managed PC, PC propagation rules and MANUAL flush mode (oh well, all the funny
      things in any JEE5 application)


      I have found two workarounds to this problem:



      1. Manually re-attach the entity delivered by the converter to the PC inside the action bean

      2. Manually create the custom converter and provide the DAO



      Workaround (2) works like this:


      Write a constructor for the converter which takes the DAO as argument
      and remembers it for later use:


          public RoleConverter (RoleDAO dao)
          {
              roleDAO = dao;
          }
      




      Provide a factory method in the Seam action bean for the converter. In
      this factory method you create the converter with the constructor mentioned
      above, providing the very same DAO which is used in the action bean itself:


          public RoleConverter getRoleConverter()
          {
              return new RoleConverter(roleDAO);
          }
      



      Use the converter in the XHTML file like this:


      <h:selectOneMenu value="#{personManager.selectedRole}" converter="#{personManager.roleConverter}" >
        <f:selectItems value="#{roleManager.allRoles}" />
      </h:selectOneMenu>
      



      That way, entities provided by the converter are attached to the same PC as the action bean is using
      and Hibernate is happy.


      I do not really like both workarounds. Both compel the designer of the UI and the backing bean to write
      special code just to make the persistence layer happy. As my design always strives to separate those
      layers as much as possible, I consider this a bad thing.


      How do others solve this problem?
      What is considered best practice in this area, given the situation I described above?


      Thank you!