11 Replies Latest reply on May 20, 2004 1:36 AM by john_anderson_ii

    Is one to many bidirectional working?

    john_anderson_ii

      I'm grinding my way though the IBM CMP tutorial, and converting the concepts to JBoss for practice. I am backending my application with MySQL.

      Everything was going smoothly with only minor glitches. Mostly due to the jboss specific XML that the IBM tutorial doesn't cover, and since I'm using XDoclet, that makes it even more complicated.

      Now to the problem, or perhaps it's a question if this is the way things are supposed to work.

      I have two business method CMP EJB's User and Group. I'll spare the full source unless it's requested, but the general gist of the CMP fields are:

      UserBean:

      public abstract String getEmail(); //primary key
      public abstract void setEmail(String email);
      public abstract String getPassword();
      public abstract void setPassword(String password);
      public abstract GroupLocal getGroupMembership(); //CMR Field with group.
      public abstract void setGroupMembership(GroupLocal groupmembership);
      
      

      GroupBean:
      public abstract String getGroupName(); //primary key
      public abstract void setGroupName(String groupname);
      public abstract String getDescription();
      public abstract void setDescription(String description);
      public abstract java.util.Collection getUsers(); //CMR field with user
      public abstract void setUsers(java.util.Collection users);
      


      I then proceeded to define the relation with:

      ejb-jar.xml
       <ejb-relation >
       <ejb-relation-name>GroupsHaveUsers</ejb-relation-name>
      
       <ejb-relationship-role >
       <ejb-relationship-role-name>UsersInGroup</ejb-relationship-role-name>
       <multiplicity>Many</multiplicity>
       <relationship-role-source >
       <ejb-name>User</ejb-name>
       </relationship-role-source>
       <cmr-field >
       <cmr-field-name>groupMembership</cmr-field-name>
       </cmr-field>
       </ejb-relationship-role>
      
       <ejb-relationship-role >
       <ejb-relationship-role-name>GroupHasUsers</ejb-relationship-role-name>
       <multiplicity>One</multiplicity>
       <relationship-role-source >
       <ejb-name>Group</ejb-name>
       </relationship-role-source>
       <cmr-field >
       <cmr-field-name>users</cmr-field-name>
       <cmr-field-type>java.util.Collection</cmr-field-type>
       </cmr-field>
       </ejb-relationship-role>
      
       </ejb-relation>
      


      jbosscmp-jdbc.xml

       <ejb-relation>
       <ejb-relation-name>GroupsHaveUsers</ejb-relation-name>
      
       <foreign-key-mapping/>
      
       <ejb-relationship-role>
       <ejb-relationship-role-name>UsersInGroup</ejb-relationship-role-name>
       <key-fields/>
      
       </ejb-relationship-role>
       <ejb-relationship-role>
       <ejb-relationship-role-name>GroupHasUsers</ejb-relationship-role-name>
       <key-fields>
       <key-field>
       <field-name>groupname</field-name>
       <column-name>groupname</column-name>
       </key-field>
       </key-fields>
      
       </ejb-relationship-role>
       </ejb-relation>
      


      Now the way I understand it the container should be handling the maintenence of this relationship right? So when I set the GroupMembership CMP field of a UserBean object, the container should add that UserObject to the corresponding Group object's Users Collection right? Or, since the relation should be bidirectional, if I add a UserLocal object to the Users Collection of a Group object then the corresponding User object's GroupMembership field should be updated with the latest GroupLocal, right?

      This is not the case. After I set a User objects GroupMembership field with a GroupLocal object, the Group objects Users Collection is still null! If I manually instatiate the Group object, create a new Collection via a type cast from ArrayList and add the User object to the Group objects collection, then it works.

      Is the relationship not working, or is this the way it should be done?



        • 1. Re: Is one to many bidirectional working?
          sesques

          Sesques,

          Thank you for taking an interest in this quirky little problem. Here is some background and relevant source.

          To setup MySQL, I removed the $JBOSS_HOME/server/default/deploy/jms/hsqldb-jdbc2-service.xml and replace it with the mysql-jdbc2-service.xml. I changed the name= tag in the mysql file to reflect the name I wanted to give to this datasource in JNDI. I made a mysql-ds.xml file that establishes connectivity and uses the default mappings from standardjbosscmp-jdbc.xml.

          There is the source of the UserBean w/ XDoclet Tags.

          /*
           * Created on May 7, 2004
           *
           * To change the template for this generated file go to
           * Window>Preferences>Java>Code Generation>Code and Comments
           */
          package com.janderson.ejb;
          
          import java.rmi.RemoteException;
          
          import javax.ejb.EJBException;
          import javax.ejb.EntityBean;
          import javax.ejb.EntityContext;
          import javax.ejb.RemoveException;
          import javax.ejb.CreateException;
          import com.janderson.interfaces.UserInfoLocal;
          
          /**
           * @author janderson
           *
           * @ejb.bean
           * name = "User"
           * display-name = "User EJB"
           * description = "Getters and Setters for User"
           * view-type = "local"
           * primkey-field = "email"
           * jndi-name = "ejb/cmptutorial/User"
           * type = "CMP"
           * cmp-version = "2.x"
           * reentrant = "true"
           * schema ="UserBean"
           *
           *
           * @ejb.finder
           * query = "SELECT Object(o) FROM UserBean o"
           * signature = "java.util.Collection findAll()"
           * unchecked = "true"
           *
           *
           *
           * To change the template for this generated type comment go to
           * Window>Preferences>Java>Code Generation>Code and Comments
           */
          public abstract class UserBean implements EntityBean {
          
          
           public UserBean() {
           super();
          
           }
          
           /**
           * @ejb.persistence
           * @ejb.pk-field
           * @ejb.interface-method
           * view-type = "local"
           * @return
           */
           public abstract String getEmail();
           /**
           * @ejb.interface-method
           * view-type = "local"
           * @param email
           */
           public abstract void setEmail(String email);
           /**
           * @ejb.persistence
           * @ejb.interface-method
           * view-type = "local"
           * @return
           */
           public abstract String getPassword();
           /**
           * @ejb.interface-method
           * view-type = "local"
           * @param password
           */
           public abstract void setPassword(String password);
           /**
           * @ejb.interface-method
           * view-type = "local"
           * @ejb.persistence
           * @ejb.relation
           * name = "GroupsHaveUsers"
           * target-ejb = "Group"
           * role-name = "UsersInGroup"
           * target-multiple = "yes"
           * cascade-delete = "no"
           * target-cascade-delete = "no"
           * target-role-name = "GroupHasUsers"
           *
           * @jboss.relation
           * related-pk-field = "groupname"
           * fk-column = "groupname"
           *
           *
           * @return
           */
           public abstract com.janderson.interfaces.GroupLocal getGroupMembership();
           /**
           * @ejb.interface-method
           * view-type = "local"
           * @param roles
           */
           public abstract void setGroupMembership(com.janderson.interfaces.GroupLocal groupmembership);
           /**
           * @ejb.create-method
           * @param email
           * @param password
           * @return
           * @throws CreateException
           */
           public String ejbCreate(String email, String password) throws CreateException{
           setEmail(email);
           setPassword(password);
           return null;
           }
          
           public void ejbPostCreate(String email, String password){}
          
          
          
           public void ejbActivate() throws EJBException, RemoteException {
          
           }
           public void ejbLoad() throws EJBException, RemoteException {
          
           }
          
           public void ejbPassivate() throws EJBException, RemoteException {
          
           }
           public void ejbRemove()
           throws RemoveException, EJBException, RemoteException {
           }
           public void ejbStore() throws EJBException, RemoteException {
           }
           public void setEntityContext(EntityContext arg0)
           throws EJBException, RemoteException {
          
           }
           public void unsetEntityContext() throws EJBException, RemoteException {
          
          
           }
          
          }
          


          Here is the code /w XDoclet tags for the GroupBean.
          /*
           * Created on May 12, 2004
           *
           * To change the template for this generated file go to
           * Window>Preferences>Java>Code Generation>Code and Comments
           */
          package com.janderson.ejb;
          
          import java.rmi.RemoteException;
          
          import javax.ejb.EJBException;
          import javax.ejb.EntityBean;
          import javax.ejb.EntityContext;
          import javax.ejb.RemoveException;
          import javax.ejb.CreateException;
          
          /**
           * @author janderson
           * @ejb.bean
           * name = "Group"
           * display-name = "Group EJB"
           * description = "Groups of Users"
           * view-type = "local"
           * primkey-field = "groupname"
           * jndi-name = "ejb/cmptutorial/Group"
           * type = "CMP"
           * cmp-version = "2.x"
           * reentrant = "false"
           * schema = "Group"
           *
           * @ejb.finder
           * query = "SELECT Object(g) FROM Group g"
           * signature = "java.util.Collection findAll()"
           * unchecked = "true"
           *
           *
           *
           * To change the template for this generated type comment go to
           * Window>Preferences>Java>Code Generation>Code and Comments
           */
          public abstract class GroupBean implements EntityBean {
          
           public GroupBean() {
           super();
          
           }
           /**
           * @ejb.persistence
           *
           * @ejb.interface-method
           * view-type = "local"
           *
           * @ejb.relation
           * name = "GroupsHaveUsers"
           * role-name = "GroupHasUsers"
           * target-ejb = "User"
           * target-multiple = "one"
           * cascade-delete = "no"
           * target-cascade-delete = "no"
           * target-role-name = "UsersInGroup"
           *
           *
           * @return
           */
           public abstract java.util.Collection getUsers();
           /**
           * @ejb.interface-method
           * view-type = "local"
           * @param users
           */
           public abstract void setUsers(java.util.Collection users);
           /**
           * @ejb.persistence
           * @ejb.interface-method
           * view-type = "local"
           * @ejb.pk-field
           * @return
           */
           public abstract String getGroupname();
           /**
           * @ejb.interface-method
           * view-type = "local"
           * @param name
           */
           public abstract void setGroupname(String groupname);
           /**
           * @ejb.persistence
           * @ejb.interface-method
           * view-type = "local"
           * @return
           */
           public abstract String getDescription();
           /**
           * @ejb.interface-method
           * view-type = "local"
           * @param description
           */
           public abstract void setDescription(String description);
          
           /**
           * @ejb.create-method
           * @param name
           * @param description
           * @return
           * @throws CreateException
           */
           public String ejbCreate(String groupname, String description) throws CreateException {
           setGroupname(groupname);
           setDescription(description);
           return null;
           }
           public void ejbPostCreate(String groupname, String description){}
          
          
          
           public void ejbActivate() throws EJBException, RemoteException {
           }
           public void ejbLoad() throws EJBException, RemoteException {
           }
           public void ejbPassivate() throws EJBException, RemoteException {
           }
           public void ejbRemove()
           throws RemoveException, EJBException, RemoteException {
           }
           public void ejbStore() throws EJBException, RemoteException {
           }
           public void setEntityContext(EntityContext arg0)
           throws EJBException, RemoteException {
           }
           public void unsetEntityContext() throws EJBException, RemoteException {
           }
          
          }
          


          Here is the revelant sections of code w/ XDoclet tags from the UserManagementBean which is acting as a facade for both User and Group beans.

           /**
           * @ejb.interface-method
           * view-type = "remote"
           * @param email
           * @param groupname
           * @return
           */
           public boolean inGroup(String email, String groupname){
           boolean b = false;
           try{
           UserLocal user = userHome.findByPrimaryKey(email);
           GroupLocal group = groupHome.findByPrimaryKey(groupname);
           b = user.getGroupMembership().getGroupname().equals(group.getGroupname());
           }catch(FinderException e){
           throw new EJBException("UserManagementBean.inGroup(String,String)" +
           "Entity " + email + " or " + groupname + "not found. Message: " + e.getMessage());
           }
           return b;
           }
           /**
           * @ejb.interface-method
           * view-type = "remote"
           * @param email
           * @param groupname
           */
           public void moveUserToGroup(String email, String groupname){
           try{
           UserLocal user = userHome.findByPrimaryKey(email);
           GroupLocal group = groupHome.findByPrimaryKey(groupname);
           Collection users = group.getUsers();
           if(users == null){
           ArrayList al = new ArrayList(50);
           al.add(user);
           group.setUsers((Collection) al);
           }else{
           group.getUsers().add(user);
           }
           }catch(FinderException e){
           throw new EJBException("UserManagementBean.moveUserToGroup. Entity " + email +" or " +
           groupname + " not found. Message: " + e.getMessage());
           }
           }
           /**
           * @ejb.interface-method
           * view-type = "remote"
           * @param groupname
           * @param rolename
           */
           public void addRoleToUsers(String groupname, String rolename){
           try{
           GroupLocal group = groupHome.findByPrimaryKey(groupname);
           RoleLocal role = roleHome.findByPrimaryKey(rolename);
           Collection users = group.getUsers();
           Iterator i = users.iterator();
           while(i.hasNext()){
           UserLocal user = (UserLocal) i.next();
           Collection userroles = user.getRoles();
           if(userroles == null){
           userroles = new ArrayList(50);
           userroles.add(role);
           }else{
           userroles.add(role);
           }
           }
           }catch(FinderException e){
           throw new EJBException("UserManagementBean.addRoleToUsers(String,String) Entity " +
           groupname + " or " + rolename + " was not found. Message: " + e.getMessage());
           }
           }
           /**
           * @ejb.interface-method
           * view-type = "remote"
           * @param groupname
           * @return
           */
           public String[] getUsersInGroup(String groupname){
           ArrayList userlist = new ArrayList(50);
           try{
           GroupLocal group = groupHome.findByPrimaryKey(groupname);
           Collection users = group.getUsers();
           Iterator itorator = users.iterator();
           while(itorator.hasNext()){
           UserLocal user = (UserLocal) itorator.next();
           StringBuffer sbUser = new StringBuffer();
           String email = user.getEmail();
           String firstname = user.getUserInfo().getFirstName();
           String lastname = user.getUserInfo().getFirstName();
           sbUser.append(email + " ");
           sbUser.append(firstname + " ");
           sbUser.append(lastname + " ");
           userlist.add(sbUser);
           }
           }catch(FinderException e){
           throw new EJBException("UserManagementBean.getUsersInGroup(String): Entity " +groupname +
           "could not be found. Message: " + e.getMessage());
           }
           return (String[]) userlist.toArray(new String[userlist.size()]);
           }
           /**
           * @ejb.interface-method
           * view-type = "remote"
           * @param groupname
           * @param description
           */
           public void addGroup(String groupname, String description){
           try{
           GroupLocal group = groupHome.create(groupname, description);
           }catch(CreateException e){
           e.printStackTrace();
           throw new EJBException("UserManagementBean.addGroup. The group " + groupname + " already"+
           " exitsts. Message: " + e.getMessage());
           }
           }
           /**
           * @ejb.interface-method
           * view-type = "remote"
           * @param groupname
           */
           public void removeGroup(String groupname){
           try{
           groupHome.remove(groupname);
           }catch(RemoveException e){
           throw new EJBException("UserManagementBean.removeGroup(): Entity " + groupname +
           " was not found. Message: " + e.getMessage());
           }
           }
           /**
           * @ejb.interface-method
           * view-type = "remote"
           * @return
           */
           public String[] getGroups(){
           String[] s;
           try{
           Collection groups = groupHome.findAll();
           if(groups == null){
           s = new String[] {"No Groups Available"};
           }else{
           Iterator i = groups.iterator();
           ArrayList groupnames = new ArrayList(50);
           while(i.hasNext()){
           GroupLocal group = (GroupLocal) i.next();
           groupnames.add(group.getGroupname());
           }
           s = (String[]) groupnames.toArray(new String[groupnames.size()]);
           }
           }catch(FinderException e){
           throw new EJBException("UserManagementBean.getGroups(): No groups were found. Message: "+
           e.getMessage());
           }
           return s;
           }
          


          And here is the relevant sections of the servlet I access the ManagementBean from.

           public void addGroup(HttpServletRequest request){
           String groupname = request.getParameter("name");
           String description = request.getParameter("description");
           try{
           UserManagement manager = home.create();
           if(groupname != null && !groupname.equals("")){
           manager.addGroup(groupname, description);
           print("<h1>Group " + groupname + " was added successfully</h1>");
           }else{
           print(groupname + " is not a valid Group name or group already exists.");
           }
           }catch(Exception e){
           out.print("There was an error adding group. Message: " + e.getMessage());
           e.printStackTrace();
           }
           }
           public void removeGroup(HttpServletRequest request){
           String groupname = request.getParameter("name");
           try{
           UserManagement manager = home.create();
           if(groupname != null && !groupname.equals("")){
           manager.removeGroup(groupname);
           print("<h1>Group was removed successfully!</h1>");
           }else{
           print("Group name specified was not valid or no longer exists.");
           }
           }catch(Exception e){
           print("There was an error processing your request. Message: " + e.getMessage());
           e.printStackTrace();
           }
           }
           public void showGroups(){
           try{
           UserManagement manager = home.create();
           String[] s = manager.getGroups();
           print("<ul>");
           for(int i=0; i<s.length; i++){
           print("<li>" + s +"</li>");
           }
           print("</ul>");
           }catch(Exception e){
           print("There was an error fetching groups, do groups exist? Message: " +e.getMessage());
           e.printStackTrace();
           }
           }
           public void joinGroup(HttpServletRequest request){
           try{
           UserManagement manager = home.create();
           String email = request.getParameter("email");
           String groupname = request.getParameter("groupname");
           if(email != null && !email.equals("") && groupname != null &&
           !groupname.equals("")){
           manager.moveUserToGroup(email, groupname);
           print("<h1>Added user " + email + " to group " + groupname + " successfully!</h1>");
           }else{
           print("<p>There was an error adding user " + email +
           " to group " + groupname + ".");
           }
           }catch(Exception e){
           print("There was an error joining groups, does user and group exist?");
           }
           }
           public void inGroup(HttpServletRequest request){
           try{
           UserManagement manager = home.create();
           String email = request.getParameter("email");
           String groupname = request.getParameter("groupname");
           if(email != null && !email.equals("") && groupname != null
           && !groupname.equals("")){
           boolean b = manager.inGroup(email,groupname);
           if(b){
           print("User " + email + " is in group " + groupname + ".");
           }else{
           print("User " + email + " is not in group " + groupname + ".");
           }
           }else{
           print("Either user " + email + " or group " + groupname + "does not exist!");
           }
           }catch(Exception e){
           print("There was an error looking up groups or users. Message: " + e.getMessage());
           e.printStackTrace();
           }
           }
           public void addRoletoGroup(HttpServletRequest request){
           try{
           UserManagement manager = home.create();
           String role = request.getParameter("role");
           String group = request.getParameter("group");
           if(role != null && !role.equals("") && group != null && !group.equals("")){
           manager.addRoleToUsers(group,role);
           print("All users in group " + group + " have been added to role " +
           role +".");
           }else{
           print("Group " + group + " or role " + role + " was not found.");
           }
           }catch(Exception e){
           print("<p>There was an error accessing group or role. Message: " + e.getMessage());
           e.printStackTrace();
           }
           }
          


          The deployment descriptor for the relationships is the same as above.

          Here is what happens. When I addUser(String email, String password,....), the MySQL table looks like this.

          email = user1@domain.com
          password = password
          ......
          groupmembership = NULL

          Then I add a group with addGroup(String name, String description), and the group (or XGroup table since group is a mysql keyword) looks like this.

          name = group1
          description = description for group1
          users = NULL

          This is all to be expected. However, when I moveUserToGroup(String email, String groupname) the tables look like this.

          User:
          email = user1@domain.com
          password = password
          groupMembership = NULL

          XGroup

          name = group1
          description = description for group1
          users = ~i

          I assume the ~i above is representative of the java.util.Collection that was inserted as a LONGBLOB. However, if the relationship was working correctly, should a UserLocal object been inserted into the userMembership column of the user table?




          • 2. Re: Is one to many bidirectional working?
            sesques

            Hi,

            How do you test that ? it works perfectly for me in production (order lines of orders).
            Post your code snippet to see what's wrong.

            Pascal

            • 3. Re: Is one to many bidirectional working?
              john_anderson_ii

              Sesques,

              Thank you for taking an interest in this quirky little problem. Here is some background and relevant source.

              To setup MySQL, I removed the $JBOSS_HOME/server/default/deploy/jms/hsqldb-jdbc2-service.xml and replace it with the mysql-jdbc2-service.xml. I changed the name= tag in the mysql file to reflect the name I wanted to give to this datasource in JNDI. I made a mysql-ds.xml file that establishes connectivity and uses the default mappings from standardjbosscmp-jdbc.xml.

              There is the source of the UserBean w/ XDoclet Tags.

              /*
               * Created on May 7, 2004
               *
               * To change the template for this generated file go to
               * Window>Preferences>Java>Code Generation>Code and Comments
               */
              package com.janderson.ejb;
              
              import java.rmi.RemoteException;
              
              import javax.ejb.EJBException;
              import javax.ejb.EntityBean;
              import javax.ejb.EntityContext;
              import javax.ejb.RemoveException;
              import javax.ejb.CreateException;
              import com.janderson.interfaces.UserInfoLocal;
              
              /**
               * @author janderson
               *
               * @ejb.bean
               * name = "User"
               * display-name = "User EJB"
               * description = "Getters and Setters for User"
               * view-type = "local"
               * primkey-field = "email"
               * jndi-name = "ejb/cmptutorial/User"
               * type = "CMP"
               * cmp-version = "2.x"
               * reentrant = "true"
               * schema ="UserBean"
               *
               *
               * @ejb.finder
               * query = "SELECT Object(o) FROM UserBean o"
               * signature = "java.util.Collection findAll()"
               * unchecked = "true"
               *
               *
               *
               * To change the template for this generated type comment go to
               * Window>Preferences>Java>Code Generation>Code and Comments
               */
              public abstract class UserBean implements EntityBean {
              
              
               public UserBean() {
               super();
              
               }
              
               /**
               * @ejb.persistence
               * @ejb.pk-field
               * @ejb.interface-method
               * view-type = "local"
               * @return
               */
               public abstract String getEmail();
               /**
               * @ejb.interface-method
               * view-type = "local"
               * @param email
               */
               public abstract void setEmail(String email);
               /**
               * @ejb.persistence
               * @ejb.interface-method
               * view-type = "local"
               * @return
               */
               public abstract String getPassword();
               /**
               * @ejb.interface-method
               * view-type = "local"
               * @param password
               */
               public abstract void setPassword(String password);
               /**
               * @ejb.interface-method
               * view-type = "local"
               * @ejb.persistence
               * @ejb.relation
               * name = "GroupsHaveUsers"
               * target-ejb = "Group"
               * role-name = "UsersInGroup"
               * target-multiple = "yes"
               * cascade-delete = "no"
               * target-cascade-delete = "no"
               * target-role-name = "GroupHasUsers"
               *
               * @jboss.relation
               * related-pk-field = "groupname"
               * fk-column = "groupname"
               *
               *
               * @return
               */
               public abstract com.janderson.interfaces.GroupLocal getGroupMembership();
               /**
               * @ejb.interface-method
               * view-type = "local"
               * @param roles
               */
               public abstract void setGroupMembership(com.janderson.interfaces.GroupLocal groupmembership);
               /**
               * @ejb.create-method
               * @param email
               * @param password
               * @return
               * @throws CreateException
               */
               public String ejbCreate(String email, String password) throws CreateException{
               setEmail(email);
               setPassword(password);
               return null;
               }
              
               public void ejbPostCreate(String email, String password){}
              
              
              
               public void ejbActivate() throws EJBException, RemoteException {
              
               }
               public void ejbLoad() throws EJBException, RemoteException {
              
               }
              
               public void ejbPassivate() throws EJBException, RemoteException {
              
               }
               public void ejbRemove()
               throws RemoveException, EJBException, RemoteException {
               }
               public void ejbStore() throws EJBException, RemoteException {
               }
               public void setEntityContext(EntityContext arg0)
               throws EJBException, RemoteException {
              
               }
               public void unsetEntityContext() throws EJBException, RemoteException {
              
              
               }
              
              }
              


              Here is the code /w XDoclet tags for the GroupBean.
              /*
               * Created on May 12, 2004
               *
               * To change the template for this generated file go to
               * Window>Preferences>Java>Code Generation>Code and Comments
               */
              package com.janderson.ejb;
              
              import java.rmi.RemoteException;
              
              import javax.ejb.EJBException;
              import javax.ejb.EntityBean;
              import javax.ejb.EntityContext;
              import javax.ejb.RemoveException;
              import javax.ejb.CreateException;
              
              /**
               * @author janderson
               * @ejb.bean
               * name = "Group"
               * display-name = "Group EJB"
               * description = "Groups of Users"
               * view-type = "local"
               * primkey-field = "groupname"
               * jndi-name = "ejb/cmptutorial/Group"
               * type = "CMP"
               * cmp-version = "2.x"
               * reentrant = "false"
               * schema = "Group"
               *
               * @ejb.finder
               * query = "SELECT Object(g) FROM Group g"
               * signature = "java.util.Collection findAll()"
               * unchecked = "true"
               *
               *
               *
               * To change the template for this generated type comment go to
               * Window>Preferences>Java>Code Generation>Code and Comments
               */
              public abstract class GroupBean implements EntityBean {
              
               public GroupBean() {
               super();
              
               }
               /**
               * @ejb.persistence
               *
               * @ejb.interface-method
               * view-type = "local"
               *
               * @ejb.relation
               * name = "GroupsHaveUsers"
               * role-name = "GroupHasUsers"
               * target-ejb = "User"
               * target-multiple = "one"
               * cascade-delete = "no"
               * target-cascade-delete = "no"
               * target-role-name = "UsersInGroup"
               *
               *
               * @return
               */
               public abstract java.util.Collection getUsers();
               /**
               * @ejb.interface-method
               * view-type = "local"
               * @param users
               */
               public abstract void setUsers(java.util.Collection users);
               /**
               * @ejb.persistence
               * @ejb.interface-method
               * view-type = "local"
               * @ejb.pk-field
               * @return
               */
               public abstract String getGroupname();
               /**
               * @ejb.interface-method
               * view-type = "local"
               * @param name
               */
               public abstract void setGroupname(String groupname);
               /**
               * @ejb.persistence
               * @ejb.interface-method
               * view-type = "local"
               * @return
               */
               public abstract String getDescription();
               /**
               * @ejb.interface-method
               * view-type = "local"
               * @param description
               */
               public abstract void setDescription(String description);
              
               /**
               * @ejb.create-method
               * @param name
               * @param description
               * @return
               * @throws CreateException
               */
               public String ejbCreate(String groupname, String description) throws CreateException {
               setGroupname(groupname);
               setDescription(description);
               return null;
               }
               public void ejbPostCreate(String groupname, String description){}
              
              
              
               public void ejbActivate() throws EJBException, RemoteException {
               }
               public void ejbLoad() throws EJBException, RemoteException {
               }
               public void ejbPassivate() throws EJBException, RemoteException {
               }
               public void ejbRemove()
               throws RemoveException, EJBException, RemoteException {
               }
               public void ejbStore() throws EJBException, RemoteException {
               }
               public void setEntityContext(EntityContext arg0)
               throws EJBException, RemoteException {
               }
               public void unsetEntityContext() throws EJBException, RemoteException {
               }
              
              }
              


              Here is the revelant sections of code w/ XDoclet tags from the UserManagementBean which is acting as a facade for both User and Group beans.

               /**
               * @ejb.interface-method
               * view-type = "remote"
               * @param email
               * @param groupname
               * @return
               */
               public boolean inGroup(String email, String groupname){
               boolean b = false;
               try{
               UserLocal user = userHome.findByPrimaryKey(email);
               GroupLocal group = groupHome.findByPrimaryKey(groupname);
               b = user.getGroupMembership().getGroupname().equals(group.getGroupname());
               }catch(FinderException e){
               throw new EJBException("UserManagementBean.inGroup(String,String)" +
               "Entity " + email + " or " + groupname + "not found. Message: " + e.getMessage());
               }
               return b;
               }
               /**
               * @ejb.interface-method
               * view-type = "remote"
               * @param email
               * @param groupname
               */
               public void moveUserToGroup(String email, String groupname){
               try{
               UserLocal user = userHome.findByPrimaryKey(email);
               GroupLocal group = groupHome.findByPrimaryKey(groupname);
               Collection users = group.getUsers();
               if(users == null){
               ArrayList al = new ArrayList(50);
               al.add(user);
               group.setUsers((Collection) al);
               }else{
               group.getUsers().add(user);
               }
               }catch(FinderException e){
               throw new EJBException("UserManagementBean.moveUserToGroup. Entity " + email +" or " +
               groupname + " not found. Message: " + e.getMessage());
               }
               }
               /**
               * @ejb.interface-method
               * view-type = "remote"
               * @param groupname
               * @param rolename
               */
               public void addRoleToUsers(String groupname, String rolename){
               try{
               GroupLocal group = groupHome.findByPrimaryKey(groupname);
               RoleLocal role = roleHome.findByPrimaryKey(rolename);
               Collection users = group.getUsers();
               Iterator i = users.iterator();
               while(i.hasNext()){
               UserLocal user = (UserLocal) i.next();
               Collection userroles = user.getRoles();
               if(userroles == null){
               userroles = new ArrayList(50);
               userroles.add(role);
               }else{
               userroles.add(role);
               }
               }
               }catch(FinderException e){
               throw new EJBException("UserManagementBean.addRoleToUsers(String,String) Entity " +
               groupname + " or " + rolename + " was not found. Message: " + e.getMessage());
               }
               }
               /**
               * @ejb.interface-method
               * view-type = "remote"
               * @param groupname
               * @return
               */
               public String[] getUsersInGroup(String groupname){
               ArrayList userlist = new ArrayList(50);
               try{
               GroupLocal group = groupHome.findByPrimaryKey(groupname);
               Collection users = group.getUsers();
               Iterator itorator = users.iterator();
               while(itorator.hasNext()){
               UserLocal user = (UserLocal) itorator.next();
               StringBuffer sbUser = new StringBuffer();
               String email = user.getEmail();
               String firstname = user.getUserInfo().getFirstName();
               String lastname = user.getUserInfo().getFirstName();
               sbUser.append(email + " ");
               sbUser.append(firstname + " ");
               sbUser.append(lastname + " ");
               userlist.add(sbUser);
               }
               }catch(FinderException e){
               throw new EJBException("UserManagementBean.getUsersInGroup(String): Entity " +groupname +
               "could not be found. Message: " + e.getMessage());
               }
               return (String[]) userlist.toArray(new String[userlist.size()]);
               }
               /**
               * @ejb.interface-method
               * view-type = "remote"
               * @param groupname
               * @param description
               */
               public void addGroup(String groupname, String description){
               try{
               GroupLocal group = groupHome.create(groupname, description);
               }catch(CreateException e){
               e.printStackTrace();
               throw new EJBException("UserManagementBean.addGroup. The group " + groupname + " already"+
               " exitsts. Message: " + e.getMessage());
               }
               }
               /**
               * @ejb.interface-method
               * view-type = "remote"
               * @param groupname
               */
               public void removeGroup(String groupname){
               try{
               groupHome.remove(groupname);
               }catch(RemoveException e){
               throw new EJBException("UserManagementBean.removeGroup(): Entity " + groupname +
               " was not found. Message: " + e.getMessage());
               }
               }
               /**
               * @ejb.interface-method
               * view-type = "remote"
               * @return
               */
               public String[] getGroups(){
               String[] s;
               try{
               Collection groups = groupHome.findAll();
               if(groups == null){
               s = new String[] {"No Groups Available"};
               }else{
               Iterator i = groups.iterator();
               ArrayList groupnames = new ArrayList(50);
               while(i.hasNext()){
               GroupLocal group = (GroupLocal) i.next();
               groupnames.add(group.getGroupname());
               }
               s = (String[]) groupnames.toArray(new String[groupnames.size()]);
               }
               }catch(FinderException e){
               throw new EJBException("UserManagementBean.getGroups(): No groups were found. Message: "+
               e.getMessage());
               }
               return s;
               }
              


              And here is the relevant sections of the servlet I access the ManagementBean from.

               public void addGroup(HttpServletRequest request){
               String groupname = request.getParameter("name");
               String description = request.getParameter("description");
               try{
               UserManagement manager = home.create();
               if(groupname != null && !groupname.equals("")){
               manager.addGroup(groupname, description);
               print("<h1>Group " + groupname + " was added successfully</h1>");
               }else{
               print(groupname + " is not a valid Group name or group already exists.");
               }
               }catch(Exception e){
               out.print("There was an error adding group. Message: " + e.getMessage());
               e.printStackTrace();
               }
               }
               public void removeGroup(HttpServletRequest request){
               String groupname = request.getParameter("name");
               try{
               UserManagement manager = home.create();
               if(groupname != null && !groupname.equals("")){
               manager.removeGroup(groupname);
               print("<h1>Group was removed successfully!</h1>");
               }else{
               print("Group name specified was not valid or no longer exists.");
               }
               }catch(Exception e){
               print("There was an error processing your request. Message: " + e.getMessage());
               e.printStackTrace();
               }
               }
               public void showGroups(){
               try{
               UserManagement manager = home.create();
               String[] s = manager.getGroups();
               print("<ul>");
               for(int i=0; i<s.length; i++){
               print("<li>" + s +"</li>");
               }
               print("</ul>");
               }catch(Exception e){
               print("There was an error fetching groups, do groups exist? Message: " +e.getMessage());
               e.printStackTrace();
               }
               }
               public void joinGroup(HttpServletRequest request){
               try{
               UserManagement manager = home.create();
               String email = request.getParameter("email");
               String groupname = request.getParameter("groupname");
               if(email != null && !email.equals("") && groupname != null &&
               !groupname.equals("")){
               manager.moveUserToGroup(email, groupname);
               print("<h1>Added user " + email + " to group " + groupname + " successfully!</h1>");
               }else{
               print("<p>There was an error adding user " + email +
               " to group " + groupname + ".");
               }
               }catch(Exception e){
               print("There was an error joining groups, does user and group exist?");
               }
               }
               public void inGroup(HttpServletRequest request){
               try{
               UserManagement manager = home.create();
               String email = request.getParameter("email");
               String groupname = request.getParameter("groupname");
               if(email != null && !email.equals("") && groupname != null
               && !groupname.equals("")){
               boolean b = manager.inGroup(email,groupname);
               if(b){
               print("User " + email + " is in group " + groupname + ".");
               }else{
               print("User " + email + " is not in group " + groupname + ".");
               }
               }else{
               print("Either user " + email + " or group " + groupname + "does not exist!");
               }
               }catch(Exception e){
               print("There was an error looking up groups or users. Message: " + e.getMessage());
               e.printStackTrace();
               }
               }
               public void addRoletoGroup(HttpServletRequest request){
               try{
               UserManagement manager = home.create();
               String role = request.getParameter("role");
               String group = request.getParameter("group");
               if(role != null && !role.equals("") && group != null && !group.equals("")){
               manager.addRoleToUsers(group,role);
               print("All users in group " + group + " have been added to role " +
               role +".");
               }else{
               print("Group " + group + " or role " + role + " was not found.");
               }
               }catch(Exception e){
               print("<p>There was an error accessing group or role. Message: " + e.getMessage());
               e.printStackTrace();
               }
               }
              


              The deployment descriptor for the relationships is the same as above.

              Here is what happens. When I addUser(String email, String password,....), the MySQL table looks like this.

              email = user1@domain.com
              password = password
              ......
              groupmembership = NULL

              Then I add a group with addGroup(String name, String description), and the group (or XGroup table since group is a mysql keyword) looks like this.

              name = group1
              description = description for group1
              users = NULL

              This is all to be expected. However, when I moveUserToGroup(String email, String groupname) the tables look like this.

              User:
              email = user1@domain.com
              password = password
              groupMembership = NULL

              XGroup

              name = group1
              description = description for group1
              users = ~i

              I assume the ~i above is representative of the java.util.Collection that was inserted as a LONGBLOB. However, if the relationship was working correctly, should a UserLocal object been inserted into the userMembership column of the user table?




              • 4. Re: Is one to many bidirectional working?
                john_anderson_ii

                Okay, let me try to break the problem down completely.

                The problem lies within these three sections of code.

                The addUser method:

                 public void addUser(String email, String password, String firstName, String middleName,
                 String lastName, String dept, String workPhone, String extension,
                 String homePhone, boolean isEmployee){
                 try{
                 UserLocal user = userHome.create(email,password);
                 UserInfoLocal info = userInfoHome.create(firstName, middleName, lastName, email, dept,
                 workPhone, extension, homePhone, isEmployee);
                 user.setUserInfo(info);
                 info.setUser(user);
                 }catch(CreateException e){
                 throw new EJBException("addUser(String,String): Failed to add user " +
                 email +". Message: " + e.getMessage());
                 }
                 }
                


                Okay, forget the UserInfo stuff entirely. When the following section of code, taken from the snippet above executes, a row is created in my datasource.

                UserLocal user = userHome.create(email,password);
                


                The row this creates in my datasource looks like

                email= user1@domain.com
                passsword= password
                groupMembership= NULL

                For now, groupMembership is null, and this is fine because we don't have any groups created yet, and this user doesn't belong to any groups.

                Now we create a group with the following section of code:
                
                 public void addGroup(String groupname, String description){
                 try{
                 GroupLocal group = groupHome.create(groupname, description);
                 }catch(CreateException e){
                 e.printStackTrace();
                 throw new EJBException("UserManagementBean.addGroup. The group " + groupname + " already"+
                 " exitsts. Message: " + e.getMessage());
                 }
                 }
                


                After the create method is called there is a row added to the XGroup table of my datasource. This row looks like.

                groupname= group1
                description= Some description string
                users= NULL

                For now the users column of this row is NULL because this group has no users assigned to it yet. That comes next.

                Now we add a user to the group with:

                 public void moveUserToGroup(String email, String groupname){
                 try{
                 UserLocal user = userHome.findByPrimaryKey(email);
                 GroupLocal group = groupHome.findByPrimaryKey(groupname);
                 Collection users = group.getUsers();
                 if(users == null){
                 ArrayList al = new ArrayList(50);
                 al.add(user);
                 group.setUsers((Collection) al);
                 }else{
                 group.getUsers().add(user);
                 }
                 }catch(FinderException e){
                 throw new EJBException("UserManagementBean.moveUserToGroup. Entity " + email +" or " +
                 groupname + " not found. Message: " + e.getMessage());
                 }
                 }
                



                This line:
                Collection users = group.getUsers();
                


                Should return the contents of table XGroup column users of the current row. Since there are no users yet in this group, the Collection returned is null. So a new Collection is created, the specified user is added to the new collection, and the new collection is set with the line:

                group.setUsers((Collection) al);
                


                You were right about the part of the above code that comes after the else part of the if statement. That code will not set anything in the database. I just haven't gotten far enough along to fix it because I'm still working on getting this relationship right. Since the Collection of users pulled from the data source is null, this code isn't executed anyway.

                Anway, back to topic: Now we have created a user a group and have added a user to a group. Let's look at the database tables

                Table: user

                email = user1@domain.com
                password = password
                groupMemberships = NULL

                Table XGroup:

                groupname = group1
                description = some description string
                users = ~i

                As we can see, a user was added to XGroup:users collection, but the relationship did not automatically update the users:groupMemberships field with the a Group object. That is the problem that I've been having.

                • 5. Re: Is one to many bidirectional working?
                  sesques

                   


                  You were right about the part of the above code that comes after the else part of the if statement. That code will not set anything in the database. I just haven't gotten far enough along to fix it because I'm still working on getting this relationship right. Since the Collection of users pulled from the data source is null, this code isn't executed anyway.


                  It's false. A collection is never null but empty. So you must test the size or the isEmpty property. So you always falls in the else clause.
                  I was wrong too. I read again the spec and the add method on the collection work. So you can always call "group.getUsers().add(user);"

                  I test exactly your config and it's work fine for me. Perhaps its a transaction configuration issue. How are configured your beans ?

                  Pascal



                  • 6. Re: Is one to many bidirectional working?
                    john_anderson_ii

                    I'm not quite sure which configuration you would like to see. But I also believe it's a mysql configuration issue, and not a JBoss CMP engine issue. Are you using a MySql back end with your applications? If so, how do you have your standardjbosscmp-jdbc.xml, mysqlds-jdbc2-service.xml, and your mysql-ds.xml files configured?

                    I'm almost sure the problems I'm having have more to do with the way the database is being handled rather than the way the beans are coded. Do you know if relationships require subqueries? I know there are some transactional type things that the current version of mysql does not support. I may try to upgrade to the devel version which does support them but is not stable.

                    The appropriate sections from ejb-jar.xml and jboss.xml are below.

                    ejb-jar.xml

                     <session >
                     <description><![CDATA[Facade for UserBean]]></description>
                     <display-name>User Management Bean</display-name>
                    
                     <ejb-name>UserManagement</ejb-name>
                    
                     <home>com.janderson.interfaces.UserManagementHome</home>
                     <remote>com.janderson.interfaces.UserManagement</remote>
                     <ejb-class>com.janderson.ejb.UserManagementBean</ejb-class>
                     <session-type>Stateless</session-type>
                     <transaction-type>Container</transaction-type>
                    
                     <ejb-local-ref >
                     <ejb-ref-name>ejb/cmptutorial/User</ejb-ref-name>
                     <ejb-ref-type>Entity</ejb-ref-type>
                     <local-home>com.janderson.interfaces.UserLocalHome</local-home>
                     <local>com.janderson.interfaces.UserLocal</local>
                     <ejb-link>User</ejb-link>
                     </ejb-local-ref>
                     <ejb-local-ref >
                     <ejb-ref-name>ejb/cmptutorial/UserInfo</ejb-ref-name>
                     <ejb-ref-type>Entity</ejb-ref-type>
                     <local-home>com.janderson.interfaces.UserInfoLocalHome</local-home>
                     <local>com.janderson.interfaces.UserInfoLocal</local>
                     <ejb-link>UserInfo</ejb-link>
                     </ejb-local-ref>
                     <ejb-local-ref >
                     <ejb-ref-name>ejb/cmptutorial/Role</ejb-ref-name>
                     <ejb-ref-type>Entity</ejb-ref-type>
                     <local-home>com.janderson.interfaces.RoleLocalHome</local-home>
                     <local>com.janderson.interfaces.RoleLocal</local>
                     <ejb-link>Role</ejb-link>
                     </ejb-local-ref>
                     <ejb-local-ref >
                     <ejb-ref-name>ejb/cmptutorial/Group</ejb-ref-name>
                     <ejb-ref-type>Entity</ejb-ref-type>
                     <local-home>com.janderson.interfaces.GroupLocalHome</local-home>
                     <local>com.janderson.interfaces.GroupLocal</local>
                     <ejb-link>Group</ejb-link>
                     </ejb-local-ref>
                    
                     </session>
                    
                     <entity >
                     <description><![CDATA[Getters and Setters for User]]></description>
                     <display-name>User EJB</display-name>
                    
                     <ejb-name>User</ejb-name>
                    
                     <local-home>com.janderson.interfaces.UserLocalHome</local-home>
                     <local>com.janderson.interfaces.UserLocal</local>
                    
                     <ejb-class>com.janderson.ejb.UserBean</ejb-class>
                     <persistence-type>Container</persistence-type>
                     <prim-key-class>java.lang.String</prim-key-class>
                     <reentrant>true</reentrant>
                     <cmp-version>2.x</cmp-version>
                     <abstract-schema-name>UserBean</abstract-schema-name>
                     <cmp-field >
                     <description><![CDATA[]]></description>
                     <field-name>email</field-name>
                     </cmp-field>
                     <cmp-field >
                     <description><![CDATA[]]></description>
                     <field-name>password</field-name>
                     </cmp-field>
                     <cmp-field >
                     <description><![CDATA[]]></description>
                     <field-name>userInfo</field-name>
                     </cmp-field>
                     <cmp-field >
                     <description><![CDATA[]]></description>
                     <field-name>roles</field-name>
                     </cmp-field>
                     <cmp-field >
                     <description><![CDATA[]]></description>
                     <field-name>groupMembership</field-name>
                     </cmp-field>
                     <primkey-field>email</primkey-field>
                    
                     <query>
                     <query-method>
                     <method-name>findAll</method-name>
                     <method-params>
                     </method-params>
                     </query-method>
                     <ejb-ql><![CDATA[SELECT Object(o) FROM UserBean o]]></ejb-ql>
                     </query>
                     <!-- Write a file named ejb-finders-UserBean.xml if you want to define extra finders. -->
                     </entity>
                     <entity >
                     <description><![CDATA[Groups of Users]]></description>
                     <display-name>Group EJB</display-name>
                    
                     <ejb-name>Group</ejb-name>
                    
                     <local-home>com.janderson.interfaces.GroupLocalHome</local-home>
                     <local>com.janderson.interfaces.GroupLocal</local>
                    
                     <ejb-class>com.janderson.ejb.GroupBean</ejb-class>
                     <persistence-type>Container</persistence-type>
                     <prim-key-class>java.lang.String</prim-key-class>
                     <reentrant>false</reentrant>
                     <cmp-version>2.x</cmp-version>
                     <abstract-schema-name>Group</abstract-schema-name>
                     <cmp-field >
                     <description><![CDATA[]]></description>
                     <field-name>users</field-name>
                     </cmp-field>
                     <cmp-field >
                     <description><![CDATA[]]></description>
                     <field-name>groupname</field-name>
                     </cmp-field>
                     <cmp-field >
                     <description><![CDATA[]]></description>
                     <field-name>description</field-name>
                     </cmp-field>
                     <primkey-field>groupname</primkey-field>
                    
                     <query>
                     <query-method>
                     <method-name>findAll</method-name>
                     <method-params>
                     </method-params>
                     </query-method>
                     <ejb-ql><![CDATA[SELECT Object(g) FROM Group g]]></ejb-ql>
                     </query>
                     <!-- Write a file named ejb-finders-GroupBean.xml if you want to define extra finders. -->
                     </entity>
                    


                    And the jboss.xml

                     <entity>
                     <ejb-name>User</ejb-name>
                     <local-jndi-name>UserLocal</local-jndi-name>
                    
                     </entity>
                     <entity>
                     <ejb-name>Group</ejb-name>
                     <local-jndi-name>GroupLocal</local-jndi-name>
                    
                     </entity>
                    
                     <session>
                     <ejb-name>UserManagement</ejb-name>
                     <jndi-name>ejb/cmptutorial/UserManagement</jndi-name>
                    
                     </session>
                    


                    • 7. Re: Is one to many bidirectional working?
                      sesques

                      No, I'm using MSSQL2000 (for the current test), Oracle and sometimes DB2. I'm not sure it is a MySQL issue.

                      I'm surprise that you don't have any @jboss.persistence tag in the bean's header. Do you correctly define the datasource-mapping to "mySQL" ?
                      Perhaps the problem is in this way, because as you says, you replace the hsqldb database with mySQL.

                      Pascal

                      • 8. Re: Is one to many bidirectional working?
                        john_anderson_ii

                        I don't really know if I'm defining the mappings correctly or not. I'm not anywhere near experienced enough with JBoss to give a definate answer. I've used the default configurations for the mappings though.

                        I can test this with MSDE/MSSQL and see if I have the same problem with MSSQL.

                        • 9. Re: Is one to many bidirectional working?
                          sesques

                          Try to add

                           * @jboss.persistence
                           * datasource-mapping="mySQL"
                           * table-name = [Same as the schema]
                          

                          in each entity header.
                          Anyway, even if you change the database, you must specify the correct database-mapping or you will experiment problems with data types.

                          Good luck, I go to sleep now.

                          Pascal


                          • 10. Re: Is one to many bidirectional working?
                            john_anderson_ii

                            Thanks a lot for the help. I appreciate the time you spent on it.

                            • 11. Re: Is one to many bidirectional working?
                              john_anderson_ii

                              Consider this one fixed guys!


                              I got a bit retarded and decided to make my CMR fields CMP fields as well. That didn't work very well.