6 Replies Latest reply on Aug 13, 2009 11:41 AM by Ben Utzer

    many to many, added element gets removed

    Ben Utzer Newbie

      Hi,


      I cannot manage to update a Set of a many-to-many relation given the code below. The problem is that while the selected gruppe is added to the gruppen-Set on the update model phase it somehow gets lost when personHome.update() is called. This is happening during a long-running conversation.


      The jUnit-test-method below executes without failure.


      What am I missing here? Any help how to best deal with this situation would be appreciated.


      Thanks,


      Ben


      Person.xhtml:


      ...
      <s:fragment rendered="#{!personHome.edit}">
              <div>Klasse</div>
              <div>#{personHome.instance.schulklasseGruppe}</div>
      </s:fragment>
       <s:fragment rendered="#{personHome.edit}">
              <label>Klasse</label>
              <h:selectOneMenu value="#{personHome.instance.schulklasseGruppe}">
                      <s:selectItems var="gruppe" value="#{schulklasseGruppenQuery.dataModel}" label="#{gruppe.kennung}">
                      </s:selectItems>
                      <s:convertEntity />
              </h:selectOneMenu>
      </s:fragment>
      ...
      <h:commandButton action="#{personHome.update}" value="Update" />
      ...



      Person.java (personHome.instance):


      @Entity
      public class Person implements Serializable {
      ...
              @Transient
              private Gruppe schulklasseGruppe;
        
              @ManyToMany()
              @JoinTable(name = "gruppe_person", joinColumns = @JoinColumn(name = "person_id"), inverseJoinColumns = @JoinColumn(name = "gruppe_id"))
              private Set<Gruppe> gruppen = new HashSet<Gruppe>();
              
              public Gruppe getSchulklasseGruppe() {
                      if(this.schulklasseGruppe == null && !this.gruppen.isEmpty()) {
                              for (Gruppe gruppe : this.gruppen) {
                                      if(Boolean.TRUE.equals(gruppe.getGruppentyp().getSchulklasse())) {
                                              this.schulklasseGruppe = gruppe;
                                      }
                              }
                      }
                      return schulklasseGruppe;
              }
      
              public void setSchulklasseGruppe(Gruppe schulklasseGruppe) {
                      if(!this.gruppen.isEmpty()) {
                              Gruppe toRemove = null;
                              for (Gruppe gruppe : this.gruppen) {
                                      if(Boolean.TRUE.equals(gruppe.getGruppentyp().getSchulklasse())) {
                                              toRemove = gruppe;
                                      }
                              }
                              if(toRemove != null) {
                                      this.gruppen.remove(toRemove);
                                      toRemove.getPersonen().remove(this);
                              }
                      }
                      if(schulklasseGruppe != null) {
                              this.gruppen.add(schulklasseGruppe);
                              schulklasseGruppe.getPersonen().add(this);
                      }
                      this.schulklasseGruppe = schulklasseGruppe;
              }
      ...
      }
      



      Gruppe.java


      @Entity
      public class Gruppe implements Serializable {
      
              private String bezeichnung;
      
              @ManyToOne(fetch = FetchType.LAZY)
              @JoinColumn(name = "gruppentyp_id")
              private Gruppentyp gruppentyp;
      
              @Id
              @Column(name = "gruppe_id")
              @GeneratedValue(strategy = GenerationType.AUTO)
              private Integer id;
      
              @ManyToMany(mappedBy = "gruppen")
              private Set<Person> personen = new HashSet<Person>();
      ...
      }
      



      jUnit-Test:



             @Test
              public void testSetSchulklasseGruppe() {
                      EntityManager em = getEntityManagerFactory().createEntityManager();
                      em.getTransaction().begin();
                      Person p = em.find(Person.class, new Integer(4218));
                      int anzahlGruppen = p.getGruppen().size();
                      Gruppe schulklasseGruppe = em.find(Gruppe.class, 13);
                      p.setSchulklasseGruppe(schulklasseGruppe);
                      em.flush();
                      em.getTransaction().commit();
                      p = em.find(Person.class, new Integer(4218));
                      assertEquals(anzahlGruppen + 1, p.getGruppen().size());
                      em.close();
              }










        • 1. Re: many to many, added element gets removed
          Ben Utzer Newbie

          What baffles me is that it also works when I replace #{personHome.instance.schulklasseGruppe} with #{personHome.schulklasseGruppe} in Person.xhtml and then delegate to #{personHome.instance} in PersonHome:


          public Gruppe getSchulklasseGruppe() {
               return getInstance().getSchulklasseGruppe();
          }
          
          public void setSchulklasseGruppe(Gruppe schulklasseGruppe) {
               this.getInstance().setSchulklasseGruppe(schulklasseGruppe);
          }
          



          • 2. Re: many to many, added element gets removed
            Ben Utzer Newbie

            Ben Utzer wrote on Aug 12, 2009 13:17:


            What baffles me is that it also works when I replace #{personHome.instance.schulklasseGruppe} with #{personHome.schulklasseGruppe} in Person.xhtml and then delegate to #{personHome.instance} in PersonHome:

            public Gruppe getSchulklasseGruppe() {
                 return getInstance().getSchulklasseGruppe();
            }
            
            public void setSchulklasseGruppe(Gruppe schulklasseGruppe) {
                 this.getInstance().setSchulklasseGruppe(schulklasseGruppe);
            }
            






            Sorry, the above is wrong. I thought I had it working but now I see it's not. I'm still searching for a solution.

            • 3. Re: many to many, added element gets removed
              Leo van den berg Master

              Ben,


              try to eager-load you collection by adding join fetch in your query (you must override the query in the Home class). helped for me! In the normal query you're only loading the colelction proxies, and that didin't work well in my application.


              Leo

              • 4. Re: many to many, added element gets removed
                Ben Utzer Newbie

                Hi Leo,


                thanks for your answer. The Home class is loading the Person object by calling find() on the persistence context in the getInstance() method. This is the default behavior as implemented in the home classes by Seam and it is just like the JUnit test is implemented.


                I would appreciate if you could share a little more of your solution. Did you overwrite the find() method with eager-loading the associated set?


                By now, I'm using a workaround that I find rather ugly. To add an element to the set, I call the setSchulklasseGruppe() method inside the update() and persist() of my PersonHome class. This bothers me as my POJO entity is not self-contained anymore and becomes fragile. The setSchulklasseGruppe() only works under obscure (for me) circumstances.


                So to me it seems, that the problem has to do with how seam and the jsf phases play together as the element can only be added in the invoke application phase but not in the update model values phase.

                • 5. Re: many to many, added element gets removed
                  Leo van den berg Master

                  Ben,


                  Just override the necessary methods in Home which in my case helped to get the collections updated. I also tried re-loading the entity prior to updating it and that also works fine (but should be necessary). At the moment I am not using EnttyHome, I am just using simple actions with an injected EntityManager. 


                  Leo

                  • 6. Re: many to many, added element gets removed
                    Ben Utzer Newbie

                    Strangely, when personHome.update() is called on PersonHome for a person who contains a schulklasseGruppe the removal of the existing schulklasseGruppe in setSchulklasseGruppe() leads to a delete in the database (using my former code and not the workaround).


                    Why is removing an element from a set is treated differently than adding an element? Why is only removing and not adding an element recognized by Seam / Hibernate?