So far, the documentation has discussed how to create simple Data Objects to access the database. These features, while sufficient to build many systems, lack the ability to relate object types to each other. To address these needs, the persistence system contains the concept of associations.
This document discusses how the persistence layer allows developers to associate Data Objects and how these associations can be saved in the database without needing to involve the Java code in how the associations are actually stored. More concretely, the document first discusses how an association is structured within PDL. It then defines the PDL for Articles, Paragraphs, and Authors and show hows to relate them through Two-Way Associations, Composite Associations, and One Way Associations.
Most commonly you associate objects to each other when both objects need to know to which objects they are associated. Magazines and Articles, Users and Groups, and Employees and Offices are all examples of this type of association. In the PDL below, an Article Object Type and an "association block" are defined to associate the Articles to Magazines. Association Block definitions are similar to Object Type definitions in that they both have attribute definitions. Instead of the Attribute being set as equal to a single column within the database, the Attribute is set as equal to a Join Path.
object type Article { BigDecimal id = articles.title; String title = articles.title; object key (articles.article_id); } // this is an "association block" associating "articles" and "magazines" association { // note that the Attribute Type is an Object Type (Article) // and not a standard Java Type. Also notice the order of the // join path and see the note below. Article[0..n] articles = join magazines.magazine_id to magazine_article_map.magazine_id, join magazine_article_map.article_id to articles.article_id; Magazine[0..n] magazines = join articles.article_id to magazine_article_map.article_id, join magazine_article_map.magazine_id to magazines.magazine_id; } |
Note | |
---|---|
The order of the join path is important. The information that the developer has must come first. That is, when the developer needs to retreive articles, the information he has is the Magazine (he needs to know for which magazine to get the articles). Therefore, the first line of the join path specifies how to join the magzines table to the mapping table. From then on, it should be in order so that the last section of the join element uses the same table as the first section of the next join element. |
Composite relationships are a special type of Association. Composite relationships are useful for modeling relationships between objects where a contained object cannot exist outside its container object. The main difference between a Composition and a standard Association is that within a Composition, one of the objects cannot exist without the other.
In the following example, a Paragraph is a component of the Article (they therefore have a composite relationship). That is, it does not make sense for a Paragraph to exist outside of an article. There are many different ways to signify a relationship as composite but the easiest way is to add the component keyword before the component Object Type (in this case, the paragraph is a component of the article)
object type Paragraph { BigDecimal id = paragraphs.paragraph_id INTEGER; String text = paragraphs.text VARCHAR(4000); object key (paragraphs.paragraph_id); } association { Article[1..1] articles = join paragraphs.article_id to articles.article_id; // notice the component keyword indicates that if the article does // not exist then the paragraph also does not exist component Paragraph[0..n] paragraphs = join articles.article_id to paragraphs.article_id; } |
Another way to make the same association is to use the composite keyword on the role that points toward the composite end of an association in order to indicate that the association is a composition. For example:
association { composite Article[1..1] articles = join paragraphs.article_id to articles.article_id; Paragraph[0..n] paragraphs = join articles.article_id to paragraphs.article_id; } |
The final way to signify the relationship is to use keywords for both object types. This displays the same behavior as the two examples above but it also valid.
association { composite Article[1..1] articles = join paragraphs.article_id to articles.article_id; component Paragraph[0..n] paragraphs = join articles.article_id to paragraphs.article_id; } |
Developers ofen only need to be able to obtain associated information in a single direction. For instance, if authors have screen names that are used and can be shared, it is useful to be able to look up the screen name for a given author. However, it may not be as important to look up the author that corresponds to a given screen name. In this case, developers should use a Role Reference. In the following example, the developer wants to be able to easily look up a given screen name for an author.
The PDL below can be used to create Object Types for both ScreenName and Author. Notice that Author contains a role reference to a ScreenName.
model tutorial; object type ScreenName { BigDecimal id = screen_names.name_id INTEGER; String screenName = screen_names.screen_name VARCHAR(200); Blob screenIcon = screen_names.screen_icon BLOB; object key (id); } object type Author { BigInteger[1..1] id = authors.author_id INTEGER; String[1..1] firstName = author.first_name VARCHAR(700); String[1..1] lastName = author.last_name VARCHAR(700); Blob[0..1] portrait = authors.portrait BLOB; // the following line is the role reference. Notice that it // appears in the definition just like an Attribute. The only // difference is that instead of pointing to a column in the ScreenName[0..1] screenName = join authors.screen_name_id to screen_names.name_id; object key (id); } |
One final feature that is immensely useful for associating objects is the idea of Link Attributes. Often, some sort of relationship is needed for associations. For instance, for Magazines, it is useful to include the page number with the Article. The concept of having Articles associated with Magazines is covered by standard associations but in order to capture a page number with the association, Link Attributes are needed.
// this is an "association block" associating "articles" and "magazines" association { Article[0..n] articles = join magazines.magazine_id to magazine_article_map.magazine_id, join magazine_article_map.article_id to articles.article_id; Magazine[0..n] magazines = join articles.article_id to magazine_article_map.article_id, join magazine_article_map.magazine_id to magazines.magazine_id; // the next line is the Link Attribute. Note that it also specifies // the SQL type of INTEGER so that the DDL generator can correctly // create the mapping table with the page_number column. BigDecimal pageNumber = magazine_article_map.page_number INTEGER; } |
For more information about Link Attributes and their use, see Section 9.8 Link Attributes.
Now that you have seen how to declare associations within PDL, you can learn the different ways to access the information from Java. In Java, Associations are accessed with two classes: DataAssociation, similar to a Java Collection, and DataAssociationCursor, similar to a Java Iterator.
public Collection getArticles(DataObject obj) { LinkedList articles = new LinkedList(); DataAssociationCursor cursor = ((DataAssociation) obj.get("articles")).cursor(); while (cursor.next()) { articles.add(cursor.getDataObject()); } return articles; } |
The next example shows how to associate one item with another. In this case, you are associating an Article with a Magazine by adding the article to the "articles" association. By calling save(), you are signalling for the data object to fire the appropriate insert and update association events (remove the "\" and make it all one line).
public void addArticle(DataObject magazine, DataObject article) { DataAssociation association = (DataAssociation) magazine.get\ ("articles"); association.add(article); magazine.save(); } |
Note | |
---|---|
There are two important things to realize when dealing with adding items to Associations and iterating through them:
|
Role References can be treated in exactly the same way as standard associations. The only practical difference between Role References and standard associations is that Role References are one-way associations and standard associations are two-way associations. Thus, everything outlined in Section 9.3.5 Using Java to Access Associations also applies to Role References.
Composite Associations are also similar to standard associations. The main difference is that in a composite association, if one item is deleted, the other does not have any real meaning (e.g., if you delete an Article, the Paragraph is meaningless).
Again, these can be accessed exactly as associations except for one significant difference: when the association between an object and its component is deleted, the component is also deleted. For example, if the association between an Article and a Paragraph were deleted, the Paragraph would be deleted. Also, when the Article is deleted, the Paragraph is deleted.