JDX Programming Code Snippets

Mapping Many-To-Many Relationships


This snippet shows how to define collection classes, join classes, and many-to-many relationships. An intermediate join class is used to hold the references for a many-to-many relationship. The snippet also shows examples of deep and shallow operations involving related objects. For a many-to-many relationship, the entities involved in the relationship are considered independent in the sense that insert, update, and delete operations don't go beyond the intermediate join class (table).

In the example object model, one user may have many groups and one group may have many users.

Class Definitions

                
                
  1. package com.softwaretree.jdxrelationshipsexample.model;
  2.  
  3. import java.util.List;
  4.  
  5. public class Group {
  6. public int gId;
  7. public String gName;
  8. public List<User> users; // A collection of User objects
  9.  
  10. public Group() {
  11. }
  12. // Other constructors and accessor (setter/getter) methods omitted
Notes:
  • A POJO (Plain Old Java Object) class with attributes of various types for a group.
    (line 5)
  • The users attribute references a collection (e.g., List) of User objects in a many-to-many relationship. We want this relationship to be of type BYVALUE, which means that, by default, the referenced collection of UserGroup objects, described later, will be inserted, updated, or deleted with a referencing Group object.
    (line 8)
                
                
  1. package com.softwaretree.jdxrelationshipsexample.model;
  2. import java.util.Vector;
  3.  
  4. public class User {
  5. public int uId;
  6. public String uName;
  7. public Vector<Group> groups; // A Collection of Group objects
  8.  
  9. public User() {
  10. }
  11. // Other constructors and accessor (setter/getter) methods omitted
Notes:
  • A POJO (Plain Old Java Object) class with attributes of various types for a group.
    (line 5)
  • The groups attribute references a collection (e.g., Vector) of Group objects in a many-to-many relationship. We want this relationship to be of type BYVALUE, which means that, by default, the referenced collection of UserGroup objects, described later, will be inserted, updated, or deleted with a referencing User object.
    (line 8)
                
                
  1. package com.softwaretree.jdxrelationshipsexample.model;
  2.  
  3. public class UserGroup {
  4. public int uId;
  5. public int gId;
  6.  
  7. public UserGroup() {
  8. }
  9.  
  10. // Other constructors and accessor (setter/getter) methods omitted
Notes:
  • A join class consisting of primary key attributes of the two classes (User and Group) involved in a many-to-many relationship.
    • This class is mainly used to define the mapping specification.
    • JDX automatically creates and manages instances of this join class.
    • Conceptually, the collection of UserGroup objects with the same value of the uId attribute corresponds to the related collection of Group objects for the User object with the given uId value. Similarly, the collection of UserGroup objects with the same value of the gId attribute corresponds to the related collection of User objects for the Group object with the given gId value.
      (line 3)

Mapping Specification

           
           
  1. JDX_OBJECT_MODEL_PACKAGE com.softwaretree.jdxandroidrelationshipsexample.model
  2.  
  3. CLASS .UserGroup TABLE USER_GRP
  4. PRIMARY_KEY uId gId
  5. ;
  6.  
  7. JOIN_COLLECTION_CLASS ListUsers COLLECTION_TYPE JAVACOLLECTION ELEMENT_CLASS .User JOIN_CLASS .UserGroup
  8. PRIMARY_KEY gId
  9. JOIN_KEY uId
  10. ORDERBY uName
  11. ;
  12.  
  13. CLASS .Group TABLE GRP
  14. PRIMARY_KEY gId
  15. RELATIONSHIP users REFERENCES ListUsers BYVALUE WITH gId
  16. ;
  17.  
  18. JOIN_COLLECTION_CLASS VectorGroups COLLECTION_TYPE VECTOR ELEMENT_CLASS .Group JOIN_CLASS .UserGroup
  19. PRIMARY_KEY uId
  20. JOIN_KEY gId
  21. ;
  22.  
  23. CLASS .User TABLE USER
  24. PRIMARY_KEY uId
  25. RELATIONSHIP groups REFERENCES VectorGroups BYVALUE WITH uId
  26. ;
Notes:
  • JDX_OBJECT_MODEL_PACKAGE specifies the default package name for all the classes whose names are specified starting with a dot (.). This is to facilitate abbreviating the class names in the mapping specification.
    (line 1)
  • This defines the mapping for the join class UserGroup that holds references for the many-to-many relationship between the User and Group classes.
    (line 3)
  • The PRIMARY_KEY of the join class is defined by the combination of primary or reference key attributes of the classes involved in the relationship.
    (line 4)
  • The JOIN_COLLECTION_CLASS specification for ListUsers defines a collection of User objects using intermediate joining objects of the UserGroup class.
    (line 7)
  • The User objects belonging to a collection for a given Group object have the same value for the attribute gId (PRIMARY_KEY) in the joining UserGroup objects.
    (line 8)
  • The JOIN_KEY defines the names (uId) of those attributes of the join class (UserGroup) whose values match the values of the corresponding attributes of all the User objects on the many-side of the relationship.
    (line 9)
  • The collection of Users will be retrieved in the order by value of uName (ORDERBY).
    (line 10)
  • The users attribute of the Group class references the previously defined ListUsers object by value (BYVALUE). This means that, by default, the referenced collection of UserGroup join objects will be inserted, updated, or deleted with the referencing Group object.
    (line 15)
  • The JOIN_COLLECTION_CLASS specification for VectorGroups defines a collection (Vector) of Group objects using intermediate joining objects of the UserGroup class.
    (line 18)
  • The Group objects belonging to a collection for a given User object have the same value for the attribute uId (PRIMARY_KEY) in the joining UserGroup objects.
    (line 19)
  • The JOIN_KEY defines the names (gId) of those attributes of the join class (UserGroup) whose values match the values of the corresponding attributes of all the Group objects on the many-side of the relationship.
    (line 20)
  • The groups attribute of the User class references the previously defined VectorGroups object by value (BYVALUE). This means that, by default, the referenced collection of UserGroup join objects will be inserted, updated, or deleted with the referencing User object.
    (line 25)

Programming Example

                
                
  1. // Obtain ORM handles.
  2. JXResource jxResource = jdxSetup.checkoutJXResource();
  3. JXSession jxSessionHandle = jxResource.getJXSessionHandle();
  4. JDXS jdxHandle = jxResource.getJDXHandle();
  5. String userClassName = User.class.getName();
  6. String groupClassName = Group.class.getName();
  7. int gId = 1;
  8. int uId = 101;
  9. String gNameBase = "group";
  10. String uNameBase = "user";
  11. // Many-to-many relationship among two classes employs an intermediate join class (table).
  12. //
  13. // If an insert, update, and delete operation on the instances of such classes is done
  14. // in a 'deep' fashion, the operation is limited to the top-level objects and the
  15. // associated join class objects. In other words, these operations do not change
  16. // the related objects in the database to avoid unintended recursion.
  17. //
  18. // A 'deep' query operation does extend upto the related objects but avoids recursion by
  19. // not fetching the related top-level original objects.
  20. try {
  21. // ********** Start a transaction to delete all the existing objects. ***********
  22. jxSessionHandle.tx_begin();
  23. // First delete all the existing User objects from the database.
  24. // This should also get rid of the associated join table entries
  25. // because of the use of the parameter JDXS.FLAG_DEEP.
  26. jdxHandle.delete2(userClassName, null, JDXS.FLAG_DEEP);
  27. // Now delete all the existing Group objects from the database.
  28. // There is no need of getting rid of the associated join table entries because
  29. // they would have already been deleted while deleting User object earlier.
  30. // Hence the use of the parameter JDXS.FLAG_SHALLOW.
  31. jdxHandle.delete2(groupClassName, null, JDXS.FLAG_SHALLOW);
  32. // Commit the transaction.
  33. jxSessionHandle.tx_commit();
  34. // ********** Now create a new Group with 2 new User objects.
  35. Group g1 = new Group(gId, gNameBase+gId);
  36. User u1 = new User(uId, uNameBase+uId);
  37. uId++;
  38. User u2 = new User(uId, uNameBase+uId);
  39. List <User> users = new ArrayList <User>();
  40. users.add(u1);
  41. users.add(u2);
  42. g1.setUsers(users);
  43. // ********** Now insert the new Group and its Users under a transaction *********
  44. jxSessionHandle.tx_begin();
  45. // Users have to be inserted separately because a many_to_many insert does
  46. // not go beyond the join table. Users can be inserted after the join table is
  47. // populated if there is no referential integrity specified
  48. // at the database level. Otherwise, user instances have to be inserted first.
  49. jdxHandle.insertMany(users, JDXS.FLAG_SHALLOW, null);
  50. // Insert the Group object now. This would also populate the join table
  51. // for the associated User objects.
  52. jdxHandle.insert(g1, JDXS.FLAG_DEEP, null);
  53. // Commit the transaction.
  54. jxSessionHandle.tx_commit();
  55. // Now do a shallow query for all the Group objects
  56. List queryResults = jdxHandle.query(groupClassName, null, -1, JDXS.FLAG_SHALLOW, null);
  57. // Now do a deep query for all the Group objects along with the associated User objects
  58. queryResults = jdxHandle.query(groupClassName, null, -1, JDXS.FLAG_DEEP, null);
  59. // Now do a shallow query for all the User objects
  60. queryResults = jdxHandle.query(userClassName, null, -1, JDXS.FLAG_SHALLOW, null);
  61. // Now do a deep query for all the User objects along with the associated Group objects
  62. queryResults = jdxHandle.query(userClassName, null, -1, JDXS.FLAG_DEEP, null);
  63. // ********** Now create a new User u3 belonging to a new Group g2.
  64. uId++;
  65. User u3 = new User(uId, uNameBase+uId);
  66. gId++;
  67. Group g2 = new Group(gId, gNameBase+gId);
  68. Vector<Group> groups = new Vector<Group>();
  69. groups.addElement(g2);
  70. u3.setGroups(groups);
  71. // ********** Now insert the new User u3 and its Group under a transaction *********
  72. jxSessionHandle.tx_begin();
  73. // First insert the Group g2
  74. jdxHandle.insert(g2, JDXS.FLAG_SHALLOW, null);
  75. // Now insert the User u3. This would also populate the join table for
  76. // the associated Group object.
  77. jdxHandle.insert(u3, JDXS.FLAG_DEEP, null);
  78. // Make the new User u3 also associated with Group g1.
  79. // The following is an alternate way of showing how existing objects can be connected
  80. // in many-to-many relationships by just populating the join object UserGroup.
  81. // We could have added Group g1 in the Vector groups above before inserting
  82. // User u3 to get the same effect.
  83. UserGroup ug = new UserGroup(u3.getuId(), g1.getgId());
  84. jdxHandle.insert(ug, JDXS.FLAG_SHALLOW, null);
  85. // Commit the transaction.
  86. jxSessionHandle.tx_commit();
  87. // At this point, we have the following situation:
  88. // Group g1 has User u1, u2, and u3.
  89. // Group g2 has User u3
  90. .
  91. // User u1 belongs to Group g1.
  92. // User u2 belongs to Group g1.
  93. // User u3 belongs to Group g1 and g2.
  94. // Now do a deep query for all the User objects along with the associated Group objects
  95. queryResults = jdxHandle.query(userClassName, null, -1, JDXS.FLAG_DEEP, null);
  96. // Now do a deep query for the Group g1 along with the associated User objects
  97. queryResults = jdxHandle.query(groupClassName, "gId=" + g1.getgId(), -1, JDXS.FLAG_DEEP, null);
  98. // Now update the User object u3 in memory and then persist the changes in the database
  99. u3.setuName("new " + u3.getuName());
  100. jdxHandle.update(u3, JDXS.FLAG_SHALLOW, null);
  101. // Now do a deep query for the Group g2 along with the associated updated User u3
  102. queryResults = jdxHandle.query(groupClassName, "gId=" + g2.getgId(), -1, JDXS.FLAG_DEEP, null);
  103. // Now do a deep delete of a Group g1. This will also delete the join table
  104. // entries for Group g1 effectively removing all its relationships with any User
  105. // objects. The deep delete operation does not delete the related User objects though.
  106. jdxHandle.delete(g1, JDXS.FLAG_DEEP, null);
  107. // At this point, we have the following situation:
  108. // Group g1 does not exist.
  109. // Group g2 has User u3.
  110. // User u1 belongs to none of the groups.
  111. // User u2 belongs to none of the groups.
  112. // User u3 belongs to Group g2.
  113. // Do a deep query for all the User objects along with the associated Group objects
  114. queryResults = jdxHandle.query(userClassName, null, -1, JDXS.FLAG_DEEP, null);
  115. // Now do a deep query for the Group objects along with the associated User objects
  116. queryResults = jdxHandle.query(groupClassName, null, -1, JDXS.FLAG_DEEP, null);
  117. } catch (Exception ex) {
  118. throw ex;
  119. } finally {
  120. jdxSetup.checkinJXResource(jxResource);
  121. }
Notes:
  • All the notes of the Simple Mapping 1 and Mapping One-To-Many Relationships Programming Examples apply.
  • As various comments in the above programming code suggest, objects with relationships can easily be manipulated using JDX APIs in a shallow or deep fashion.