2 Replies Latest reply on Dec 10, 2007 3:55 AM by avpavlov

    ManyToMany association within conversation scope bean

    avpavlov

      Hi all.

      I have 2 entities linked via many-to-many association

      @Entity
      @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
      public class User implements Serializable {
       @Id
       @GeneratedValue
       private long id;
      
       @NotEmpty
       @Column(unique = true)
       private String username;
      
       @ManyToMany(cascade = CascadeType.ALL)
       @JoinTable(
       name="UserToProvider",
       joinColumns = { @JoinColumn( name="user_id") },
       inverseJoinColumns = @JoinColumn( name="provider_id")
       )
       private Collection<Provider> providers = new ArrayList<Provider>();
      }
      
      @Entity
      @Table(name="Provider" )
      @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
      public class Provider implements Serializable {
       @Id
       @GeneratedValue
       private int id;
       @NotNull
       private String UPI; //Unique Provider identifier
      
       @ManyToMany(mappedBy="providers", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
       private Collection<User> users;
      }
      


      Users list displayed on Provider form
      ....
       <h:panelGrid title="Users">
       <ui:repeat var="user" value="#{providerFormAction.provider.users}">
       <h:commandLink value="#{user.firstName} #{user.lastName}" action="#{userFormAction.view(user)}"/>
       <br/>
       </ui:repeat>
       <h:commandButton action="#{userFormAction.addNew}" value="Add new"/>
      
      ...
      
      



      userFormAction.addNew() and .view() methods are marked as starting nested conversation. Save() method finihes current converastion and restore parent.
      public class UserFormAction implements java.io.Serializable {
       @In
       EntityManager entityManager = null;
      
       @In
       Conversation conversation;
      
       private User user = null;
      
       @Begin(nested=true)
       public String addNew() {
       user = new User();
       return "/jspx/userForm.jspx";
       }
      
       @Begin(nested=true)
       public String view(User u) {
       user = u;
       return "/jspx/userForm.jspx";
       }
      
       public void save() {
       entityManager.persist(user);
      
       // tried flush - it did not help!!!
       // entityManager.flush();
      
       // to return to previous conversation
       conversation.endAndRedirect();
       }
      
       ....
      


      After returning to Provider form it is still displaying OLD links (i.e. if I created new user linked to this provider - it is not displayed, if I removed link - it is still displayed as linked), although username is refreshed!

      If I clear conversationId in browser address bar then links become correct.

      Looks like marked as "mappedBy" end of many-to-many association is not refreshed properly in Seam - I tested other fields type - simple properties, ManyToOne, active end of ManyToMany - all refreshed properly OR I'M WRONG?.

        • 1. Re: ManyToMany association within conversation scope bean
          pmuir

          Seam is not caching part of your entity - perhaps hibernate is though in the persistence context?

          • 2. Re: ManyToMany association within conversation scope bean
            avpavlov

             

            "pete.muir@jboss.org" wrote:
            ... hibernate is though in the persistence context?


            I'm not sure I understand that :) Could you please clarify? Do you mean hibernate fetched collection to Provider bean and did not refresh when User form saved (== links changed)? If yes, why usernames were refreshed - these referenced from collection and refreshed properly.

            I agree that probaly I'm wrong when blame Seam but there are a lot of annotations and I assumed that some combination (or simply missed) annotations lead to this side effect.

            At the moment I "brute-forced" it :) - set event listener to post-ins/upd/del event, register provider form method as event observer and check for collections inconsistencies
             if (r instanceof User) {
             User u = (User)r;
             if (getProvider().getUsers().contains(u) != u.getProviders().contains(getProvider())) {
             entityManager.refresh(getProvider());
             }
             }
            

            It works but I'm sure there is better solution (at least not so "in-place").