9.8. Link Attributes

When modeling many real-world problems, it is common to encounter situations that require associating data with a link between two objects. This document describes how to model these situations using the persistence system.

9.8.1. Introduction

When creating a model for use in a particular domain, there are usually cases in which a simple association between two object types will not suffice. A Group may contain Users through a members Association (see Section 9.3 Associations), and for each member that Group may want to record the date on which the User became a member of the Group. This is a case in which the link between two objects must carry more information than just that the two objects are associated. The UML terminology for this is a qualified association and it is described with the following model.

      ____________                           ____________ 
     |   Group    |                         |    User    |
     |------------|                         |------------|
     |            | n                     n |            |
     |            |------------------------>|            |
     |            |           |     members |            |
     |____________|           |             |____________|
                              |
                              |
                              |
                      ________|________ 
                     |                 |
                     |-----------------|
                     |  membershipDate |
                     |                 |
                     |_________________|

A model like the one depicted above can be created in sql with the following 3 tables.

create table groups (
      group_id     integer primary key;
      name         varchar(300);
      ....
);

create table users (
      user_id      integer primary key;
      name         varchar(300);
      email        varchar(100);
      ....
);

create table group_member_map (
      group_id     integer references groups;
      member_id    integer references users;
      membership_date date default sysdate;
      primary key (group_id, member_id);
);

This association can then easily be transformed to PDL through the use of the association keyword. When defining an association between two objects using the association block, it is possible to add extra properties that can store information associated with a particular link between two objects.

    object type Group {
        BigInteger[1..1] id = groups.group_id;
        String[1..1]     name = groups.name;
        // ...
    }

    object type User {
        BigInteger[1..1] id = users.user_id;
        String[1..1]     name = users.name;
        String[1..1]     email = users.email;
        // ...
    }

    association {
        Group[0..n] groups = join users.user_id to \
group_member_map.member_id,
                             join group_member_map.group_id to \
groups.group_id;
        User[0..n]  members = join groups.group_id to \
group_member_map.group_id,
                              join group_member_map.member_id to \
users.user_id;

        Date[1..1]  membershipDate = group_member_map.membership_date;
    }

NoteNote
 

In this case, all events are auto-generated and will only work if the link attribute (in this case, membershipDate) is defined within the mapping table. If the DDL generator is used, the above association definition will create the correct mapping table.

It is also possible to have a link attribute that returns a full object type (e.g. another User) rather than a simple object type (e.g. a Date). Everything in this example is the same if you replace Date with User. The only extra assumption made is that the full object type (e.g. User) only has a single Object Key and that key is the item stored in the mapping table (e.g. user_id must be stored in the mapping table in the case of User).

9.8.2. Link Attributes API

Once the PDL has been written to describe link attributes, the java API may access the values using the following methods (remove the "\" and make it all one line):

9.8.2.1. Creating and Initializing Links

The DataAssociation.add(DataObject object) method is used when creating a link between two objects. If the association was defined with the association keyword, this method returns the link DataObject. The link DataObject can then be used to set any link attributes that have been defined. This is illustrated in the following example:

      // Put the current session in a variable for convenience.
      Session ssn = SessionManager.getSession();

      // Get a user.
      DataObject user = ssn.retrieve(new OID("User", ...));
      // Get a group.
      DataObject group = ssn.retrieve(new OID("Group", ...));

      // Get the "members" association so that we can add a member to the
      // group.
      DataAssociation members = (DataAssociation) group.get("members");

      // Add the user to the association.
      DataObject link = members.add(user);
      java.util.Date now = new java.util.Date();
      link.set("membershipDate", now);

      // Persist the changes.
      group.save();

NoteNote
 

Only group.save() is called to persist the changes. Changes to the links are automatically persisted when the parent object is saved.

9.8.2.2. Reading Link Attributes

The DataAssociationCursor.getLinkProperty(java.lang.String) method can be used to read the values of a link attribute while iterating over a cursor.

      // Put the current session in a variable for convenience.
      Session ssn = SessionManager.getSession();

      // Get a group.
      DataObject group = ssn.retrieve(new OID("Group", ...));

      // Get the members association.
      DataAssociation members = (DataAssociation) group.get("members");

      // Iterate over the members of the group and print out the
      // membership date.
      DataAssociationCursor cursor = members.getDataAssociationCursor();
      while (cursor.next()) {
          // Fetch the users email for the current row.
          String email = cursor.get("email");
          // Fetch the link property for the current row.
          java.util.Date membershipDate =
              (java.util.Date) cursor.getLinkProperty("membershipDate");

          System.out.println(email + ": " + membershipDate);
      }

9.8.2.3. Updating Link Attributes

The DataAssociationCursor.getLink() method can be used to fetch the DataObject that represents a link. This DataObject can then be used to modify the link and persist the results.

      // Put the current session in a variable for convenience.
      Session ssn = SessionManager.getSession();

      // Get a group.
      DataObject group = ssn.retrieve(new OID("Group", ...));

      // Get the members association.
      DataAssociation members = (DataAssociation) group.get("members");

      // Iterate over the members of the group and modify each link.
      DataAssociationCursor cursor = members.getDataAssociationCursor();
      while (cursor.next()) {
          // Fetch the link object for the current row.
          DataObject link = cursor.getLink();

          // Get the membership date from the link data object.
          java.util.Date oldDate = (java.util.Date) \
link.get("membershipDate");

          // Incrememnt the membership date by 1 millisecond.
          java.util.Date newDate = new \
java.util.Date(oldDate.getTime() + 1);

          // Set the new date
          link.set("membershipDate", newDate);
      }

      // Persist all the changed links.
      group.save();

NoteNote
 

Only group.save() is called to persist the changes. Changes to the links are automatically persisted when the parent object is saved.

9.8.2.4. Filtering and Ordering based on Link Attributes

To access the values of a link attribute when filtering or setting the order of a data association, simply put the special link prefix in front of the link attribute name.

      // Put the current session in a variable for convenience.
      Session ssn = SessionManager.getSession();

      // Get a group.
      DataObject group = ssn.retrieve(new OID("Group", ...));

      // Get the members association.
      DataAssociation members = (DataAssociation) group.get("members");

      // Iterate over new members of the group.
      DataAssociationCursor cursor = members.getDataAssociationCursor();

      // Get yesterday's date.
      java.util.Date yesterday =
          new java.util.Date(System.currentTimeMilis() - \
1000 * 60 * 60 * 24);

      // Restrict to recent members
      Filter f = cursor.addFilter("link.membershipDate > :date");
      f.set("date", yesterday);

      // Order by membership date, descending
      cursor.setOrder("link.membershipDate desc");

      while (cursor.next()) {
          ...
      }