1 2 Previous Next 17 Replies Latest reply on Nov 9, 2009 11:43 AM by wolfgangknauf

    ManyToMany problem

      Hi all

      playing around with Seam and trying to fill a manytomany join table. Following situation: A person has multiple roles (based on work, not as a role known from the security). A role could have 0..n functions (specialised education). so i tried the follwing:

      Role and Funtion joined on role_function (join table, for the moment only role_id and function_id, in future with a generated id) The qualification table contains person_id and role_function_id.

      Now the questions:

      1) How to autogenerate an role_function_id on assigning a function to a role?
      2) Do i have to develop a seperate bean for the qualification?

      Sorry if the question is clear for all. Searched for the answer and didn't find something which will helps out a newbie like me! Hope someone will clear my questions. Thanks in advance.

        • 1. Re: ManyToMany problem
          wolfgangknauf

          Hi,

          I assume that you want to use EJB3/JPA for creating your entities?

          In this case, you don't need a role_function entity, and the server will autogenerate the database table for you. The entity "Role" will have a list of "Functions", and the "Function" will have a list of "Role", both connected by annotations.

          Pseudocode:

          @Entity
          public class Role implements Serializable
          {
           @ManyToMany (mappedBy="roles")
           public List<Function> functions;
          }
          
          @Entity
          public class Function implements Serializable
          {
           @ManyToMany ()
           public List<Role> roles;
          }
          


          Then, your code can access the functions of a role by "myRole.functions.get(x);". The rest is the job of the server and you don't have to care much about it.

          Of course, there is quite a bit more to keep in mind. First of all, the two fields should have a getter and setter. More detailed information can be found here: http://www.jboss.org/community/wiki/EJB3relationships

          Hope this helps

          Wolfgang

          • 2. Re: ManyToMany problem

            Hi Wolfgang

            Thanks for your input.
            So far i got it already. The role_function is generated automaticly

            But now i got
            role_id | function_id

            What should i do if like a generated id within this table?

            Thanks a lot

            • 3. Re: ManyToMany problem
              wolfgangknauf

              Hi,

              basically, you don't need a generated ID for the mapping table because the pair "role_id/function_id" is already a valid primary key.
              If you need a id column anyway, you have to build a mapping entity with a generated ID property and two @OneToMany relationship fields to Role and Function.

              So your code for mapping a function to a role could be:

              RoleToFunction role2FunctionNew = new RoleToFunction();
              role2FunctionNew.setFunction(...);
              role2FunctionNew.setRole(...);
              
              role.getFunctionmappings().add (role2FunctionNew);
              


              Hope this helps

              Wolfgang

              • 4. Re: ManyToMany problem

                Hi Wolfgang

                Thanks for your input.
                I need the generated id to reference to this pair (role - function).

                I tried to do it as it is described here:
                http://en.wikibooks.org/wiki/Java_Persistence/ManyToMany#Additional_columns_in_join_table.

                for the moment it doesn't work. I'll keep going on this way.
                Perhaps i will return a few hours later again with more questions.

                • 5. Re: ManyToMany problem

                  Sorry :)

                  I just stuck here and have no idea what the problem is.

                  Hope someone got input for me!

                  Here my classes:

                  package ch.emtm.entity;
                  
                  import java.io.Serializable;
                  import java.util.List;
                  
                  import javax.persistence.Entity;
                  import javax.persistence.GeneratedValue;
                  import javax.persistence.Id;
                  import javax.persistence.JoinColumn;
                  import javax.persistence.OneToMany;
                  import javax.persistence.OneToOne;
                  import javax.persistence.Version;
                  
                  import org.hibernate.validator.Length;
                  import org.hibernate.validator.NotNull;
                  import org.jboss.seam.annotations.Name;
                  
                  @Entity
                  @Name("role")
                  public class Role implements Serializable
                  {
                   /**
                   *
                   */
                   private static final long serialVersionUID = 1L;
                  
                   @Id
                   @GeneratedValue
                   private Long id;
                   @Version
                   private Integer version;
                   @Length(max=64)
                   @NotNull
                   private String name;
                   @OneToMany(mappedBy="role")
                   private List<RoleFunction> functions;
                   @NotNull
                   @OneToOne
                   @JoinColumn(name = "status")
                   private RecordStatus recordStatus = new RecordStatus();
                  
                   /**
                   * @return the id
                   */
                   public Long getId() {
                   return id;
                   }
                   /**
                   * @param id the id to set
                   */
                   public void setId(Long id) {
                   this.id = id;
                   }
                   /**
                   * @return the version
                   */
                   public Integer getVersion() {
                   return version;
                   }
                   /**
                   * @param version the version to set
                   */
                   public void setVersion(Integer version) {
                   this.version = version;
                   }
                   /**
                   * @return the name
                   */
                   public String getName() {
                   return name;
                   }
                   /**
                   * @param name the name to set
                   */
                   public void setName(String name) {
                   this.name = name;
                   }
                   /**
                   * @return the functions
                   */
                   public List<RoleFunction> getFunctions() {
                   return functions;
                   }
                   /**
                   * @param functions the functions to set
                   */
                   public void setFunctions(List<RoleFunction> functions) {
                   this.functions = functions;
                   }
                   /**
                   * @return the recordStatus
                   */
                   public RecordStatus getRecordStatus() {
                   return recordStatus;
                   }
                   /**
                   * @param recordStatus the recordStatus to set
                   */
                   public void setRecordStatus(RecordStatus recordStatus) {
                   this.recordStatus = recordStatus;
                   }
                  
                  }
                  


                  package ch.emtm.entity;
                  
                  import java.io.Serializable;
                  import java.util.ArrayList;
                  import java.util.List;
                  
                  import javax.persistence.Entity;
                  import javax.persistence.GeneratedValue;
                  import javax.persistence.Id;
                  import javax.persistence.JoinColumn;
                  import javax.persistence.OneToMany;
                  import javax.persistence.OneToOne;
                  import javax.persistence.Version;
                  
                  import org.hibernate.validator.Length;
                  import org.hibernate.validator.NotNull;
                  import org.jboss.seam.annotations.Name;
                  
                  @Entity
                  @Name("function")
                  public class Function implements Serializable
                  {
                   /**
                   *
                   */
                   private static final long serialVersionUID = 1L;
                  
                   @Id
                   @GeneratedValue
                   private Long id;
                   @Version
                   private Integer version;
                   @Length(max = 64)
                   @NotNull
                   private String name;
                   @OneToMany(mappedBy="function")
                   private List<RoleFunction> roles = new ArrayList<RoleFunction>();
                   @NotNull
                   @OneToOne
                   @JoinColumn(name = "status")
                   private RecordStatus recordStatus = new RecordStatus();
                  
                   public Long getId() {
                   return id;
                   }
                  
                   public void setId(Long id) {
                   this.id = id;
                   }
                  
                   public Integer getVersion() {
                   return version;
                   }
                  
                   @SuppressWarnings("unused")
                   private void setVersion(Integer version) {
                   this.version = version;
                   }
                  
                   public String getName() {
                   return name;
                   }
                  
                   public void setName(String name) {
                   this.name = name;
                   }
                  
                   /**
                   * @return the roles
                   */
                   public List<RoleFunction> getRoles() {
                   return roles;
                   }
                  
                   /**
                   * @param roles the roles to set
                   */
                   public void setRoles(List<RoleFunction> roles) {
                   this.roles = roles;
                   }
                  
                   /**
                   * @return the recordStatus
                   */
                   public RecordStatus getRecordStatus() {
                   return recordStatus;
                   }
                  
                   /**
                   * @param recordStatus the recordStatus to set
                   */
                   public void setRecordStatus(RecordStatus recordStatus) {
                   this.recordStatus = recordStatus;
                   }
                  
                  }
                  


                  package ch.emtm.entity;
                  
                  import java.io.Serializable;
                  
                  import javax.persistence.Entity;
                  import javax.persistence.GeneratedValue;
                  import javax.persistence.Id;
                  import javax.persistence.ManyToOne;
                  import javax.persistence.PrimaryKeyJoinColumn;
                  
                  import org.jboss.seam.annotations.Name;
                  
                  @Entity
                  @Name("rolefunction")
                  public class RoleFunction implements Serializable
                  {
                   /**
                   *
                   */
                   private static final long serialVersionUID = 1L;
                  
                   @Id
                   @GeneratedValue
                   private Long id;
                   @ManyToOne()
                   @PrimaryKeyJoinColumn(name = "function", referencedColumnName = "id")
                   private Function function = new Function();
                   @ManyToOne()
                   @PrimaryKeyJoinColumn(name = "role", referencedColumnName = "id")
                   private Role role = new Role();
                  // @OneToMany(mappedBy="roleFunction")
                  // private List<Qualification> qualifications = new ArrayList<Qualification>();
                  
                   /**
                   * @return the id
                   */
                   public Long getId() {
                   return id;
                   }
                   /**
                   * @param id the id to set
                   */
                   public void setId(Long id) {
                   this.id = id;
                   }
                   /**
                   * @return the roleId
                   */
                   public Role getRole() {
                   return role;
                   }
                   /**
                   * @param roleId the roleId to set
                   */
                   public void setRole(Role role) {
                   this.role = role;
                   }
                   /**
                   * @return the functionId
                   */
                   public Function getFunction() {
                   return function;
                   }
                   /**
                   * @param functionId the functionId to set
                   */
                   public void setFunction(Function function) {
                   this.function = function;
                   }
                  // /**
                  // * @return the qualifications
                  // */
                  // public List<Qualification> getQualifications() {
                  // return qualifications;
                  // }
                  // /**
                  // * @param qualifications the qualifications to set
                  // */
                  // public void setQualifications(List<Qualification> qualifications) {
                  // this.qualifications = qualifications;
                  // }
                  
                  }
                  


                  So i get an error:

                  21:20:01,733 SEVERE [application] java.lang.NullPointerException
                  javax.faces.el.EvaluationException: java.lang.NullPointerException
                   at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:102)
                   at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
                   at javax.faces.component.UICommand.broadcast(UICommand.java:387)
                   at org.ajax4jsf.component.AjaxViewRoot.processEvents(AjaxViewRoot.java:321)
                   at org.ajax4jsf.component.AjaxViewRoot.broadcastEvents(AjaxViewRoot.java:296)
                   at org.ajax4jsf.component.AjaxViewRoot.processPhase(AjaxViewRoot.java:253)
                   at org.ajax4jsf.component.AjaxViewRoot.processApplication(AjaxViewRoot.java:466)
                   at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:82)
                   at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:100)
                   at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
                   at javax.faces.webapp.FacesServlet.service(FacesServlet.java:265)
                   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
                   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                   at org.ajax4jsf.webapp.BaseFilter.doFilter(BaseFilter.java:510)
                   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
                   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                   at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:83)
                   at org.jboss.seam.web.IdentityFilter.doFilter(IdentityFilter.java:40)
                   at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
                   at org.jboss.seam.web.MultipartFilter.doFilter(MultipartFilter.java:90)
                   at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
                   at org.jboss.seam.web.ExceptionFilter.doFilter(ExceptionFilter.java:64)
                   at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
                   at org.jboss.seam.web.RedirectFilter.doFilter(RedirectFilter.java:45)
                   at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
                   at org.ajax4jsf.webapp.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:178)
                   at org.ajax4jsf.webapp.BaseFilter.handleRequest(BaseFilter.java:290)
                   at org.ajax4jsf.webapp.BaseFilter.processUploadsAndHandleRequest(BaseFilter.java:368)
                   at org.ajax4jsf.webapp.BaseFilter.doFilter(BaseFilter.java:495)
                   at org.jboss.seam.web.Ajax4jsfFilter.doFilter(Ajax4jsfFilter.java:56)
                   at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
                   at org.jboss.seam.web.LoggingFilter.doFilter(LoggingFilter.java:60)
                   at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
                   at org.jboss.seam.web.HotDeployFilter.doFilter(HotDeployFilter.java:53)
                   at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
                   at org.jboss.seam.servlet.SeamFilter.doFilter(SeamFilter.java:158)
                   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
                   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                   at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
                   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
                   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                   at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:235)
                   at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
                   at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:190)
                   at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:433)
                   at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:92)
                   at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.process(SecurityContextEstablishmentValve.java:126)
                   at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.invoke(SecurityContextEstablishmentValve.java:70)
                   at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
                   at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
                   at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:158)
                   at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
                   at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:330)
                   at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:829)
                   at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:598)
                   at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
                   at java.lang.Thread.run(Thread.java:619)
                  Caused by: java.lang.NullPointerException
                   at ch.emtm.session.RoleHome.update(RoleHome.java:62)
                  


                  • 6. Re: ManyToMany problem

                    one step closer :D

                    i got the problem - at least i think i'm close to!
                    on roleList.xhtml i got:

                    <s:decorate id="functionField" template="layout/edit.xhtml">
                    <ui:define name="label">Function</ui:define> <h:selectManyCheckbox id="functions" value="#{roleHome.instance.functions}" layout="pageDirection">
                    <s:selectItems value="#{functionList.resultList}" var="function" label="#{function.name}" />
                    <s:convertEntity />
                    </h:selectManyCheckbox>
                    </s:decorate>


                    and when i save i receive the following error:
                    23:33:05,776 SEVERE [application] java.lang.ClassCastException: ch.emtm.entity.Function cannot be cast to ch.emtm.entity.RoleFunction


                    i understood the problem, but for the moment i have no idea how to solve.
                    i think your code posted above with the new rolefunction() will solve - but i couldn't get.

                    Do you have a hint for me ;)

                    Thanks in advance

                    • 7. Re: ManyToMany problem

                      Hi again :D

                      i found a solution - at least i'm close to a solution.

                      I added this function to the Role.

                      @OneToMany(mappedBy="role")
                      private List<RoleFunction> listRoleFunctions;
                      ...
                      public List<Function> getFunctions(){
                       List<Function> functions = new ArrayList<Function>();
                       if(this.listRoleFunctions != null){
                       for(RoleFunction rf:this.listRoleFunctions){
                       Function f = new Function();
                       f = rf.getFunction();
                       functions.add(f);
                       }
                       }
                       return functions;
                       }
                      
                       public void setFunctions(List<Function> functions){
                       if(functions.size() > this.listRoleFunctions.size()){
                       //New function to add
                       for(Function f:functions){
                       boolean add = true;
                       for(RoleFunction roleFunction:this.listRoleFunctions){
                       if(roleFunction.getId().equals(f.getId())){
                       add = false;
                       break;
                       }
                      
                       }
                       if(add){
                       RoleFunction rf = new RoleFunction();
                       rf.setFunction(f);
                       rf.setRole(this);
                       System.out.println("Adding (rf from lRF)"+rf.getRole().getName() +"."+rf.getFunction().getName());
                       this.listRoleFunctions.add(rf);
                       }
                       }
                       }else{
                       //Function to remove
                       System.out.println("remove function");
                       for(RoleFunction roleFunction:this.listRoleFunctions){
                       boolean remove = true;
                       for(Function f:functions){
                       if(roleFunction.getId().equals(f.getId())){
                       remove = false;
                       break;
                       }
                       }
                       if(remove){
                       this.listRoleFunctions.remove(roleFunction);
                       }
                       }
                       }
                       }
                      


                      The only thing which doesn't work is if a user deselect a already saved function assignment.

                      I always get this error:
                      10:49:58,960 INFO [STDOUT] remove function
                      10:49:58,960 SEVERE [component] /roleList.xhtml @72,106 value="#{roleHome.instance.functions}": Error writing 'functions' on type ch.emtm.entity.Role
                      java.util.ConcurrentModificationException
                       at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
                       at java.util.AbstractList$Itr.next(AbstractList.java:343)
                       at org.hibernate.collection.AbstractPersistentCollection$IteratorProxy.next(AbstractPersistentCollection.java:577)
                      ...


                      Hope someone got input to solve this issue!
                      Thanks in advance for any hint

                      Greets

                      • 8. Re: ManyToMany problem
                        wolfgangknauf

                        Hi,

                        that's a standard iterator problem, no EJB3 issue ;-):

                        for(RoleFunction roleFunction:this.listRoleFunctions){
                         ...
                         this.listRoleFunctions.remove(roleFunction);

                        You remove an item from the list over which you iterate. Use this "for (int index = this.listRoleFunctions.size() - 1; index >= 0; index--)" to avoid this.

                        Best regards

                        Wolfgang

                        • 9. Re: ManyToMany problem

                          Booah thanks for the fast answer :D

                          New to all the things here - i'm so sorry!

                          Next question i got!
                          After i remover the roleFunction from the listRoleFunctions (i've checked, it's okay) and save the removed item is still available in the database!
                          Bad news: there's no stack trace neither in console nor server.log!

                          Hope this issue is as simple to solve as the last one =)

                          Thanks for your assistance!

                          Best regards, Sven

                          • 10. Re: ManyToMany problem
                            wolfgangknauf

                            Hi Sven (from Germany?),

                            another fast answer, but it will give you a bit to read and think about: http://www.jboss.org/community/wiki/EJB3relationships
                            I hope the section "Managing the (bidirectional) relationship" will provide you with explanations on how to fix this.

                            Wolfgang

                            • 11. Re: ManyToMany problem
                              wolfgangknauf

                              I see that I posted this link already earlier in this thread. Sorry for this duplicate ;-)!

                              Wolfgang

                              • 12. Re: ManyToMany problem

                                Hi Wolfgang

                                close to Germany from [ + ] ;)

                                thanks i will have a look at it again - hope to get it work now.
                                Will return if i get questions or if it's done.

                                • 13. Re: ManyToMany problem

                                  Sorry back again *grml*

                                  I don't get it - cause i'm doing the assignment in the entity not in the bean. Imho there's a difference to the text you've wrote.

                                  I tried to remove the refernces within the remove loop from both sides. No effect =(

                                  • 14. Re: ManyToMany problem
                                    wolfgangknauf

                                    Hi,

                                    could you post the updated code which removes from the relationship, and also the relationship related snippets of the entities? There were so many changes that I need a full update of it ;-).

                                    I think it is better to keep this code in the session bean, because you are manipulating more than one entity with it.

                                    Best regards

                                    Wolfgang

                                    1 2 Previous Next