Tuesday, July 5, 2011

One-to-Many, Unidirectional Associations

This relationship is essentially the same as Many to One relation, Unidirectional relation , but now we choose the opposite direction for navigating the connection. See here for Many to one unidirectional relation.

Thus our Lecturer object now has a property which is a collection of Student objects while the Student objects have no properties which refer to their supervising Lecturer.

A One-to-Many, Unidirectional Association

As far as the database is concerned, there is no difference between this and the unidirectional many-to-one association: there will still be a single column in the table holding the Student objects that contains a foreign key into the table holding the Lecturer objects.
Now that entities are being stored in collections, it becomes critical that you have appropriately implemented equals and hashCode methods for those entities. In particular, you should ensure that these methods are independent of the generated surrogate keys and that trivial changes to the object do not effect the methods while the objects are in the collections.
The simplest collection, for our purposes, is a Set. To create the association, we add a Set valued property to Lecturer
<set name="advisees" cascade="save-update" lazy="true">
    <key column="lecturer_id"/>
    <one-to-many class="Student"/>

Here, we define a property of Lecturer which is Set valued. The name of the property is advisees. This property is to capture a one-to-many association to the Student class and it this association is to be implemented in the database as a foreign key to the table holding Lecturer objects stored in the column lecturer_id in the table holding Student objects.
There are a number of constraints imposed by the use of this one-to-many association which arises from the fact that it is represented by this "reverse" link from the contained object side of the association:
  1. From a Java point of view, we could potentially have two different Lecturer objects, both of which have the same Student object in their container. However, this is not possible for a one-to-many association because, in the database, each Student row refers to the single Lecturer row which contains it. If you want the true Java semantics, you have to represent the association as a many-to-many one.
  2. You cannot have the same object multiple times in the same collection. This is obvious when the collection property is of type Set, but one could use other types, such as List. However, the implementation of association by the reverse foreign key makes this impossible. Again, a many-to-many association can provide the appropriate semantics

Finally, there is the question of why one-to-many associations between entities cause problems. Consider the following code:
tx = session.beginTransaction();
Lecturer lect = new Lecturer("Gordon Brown") ;
lect.getAdvisees().add(new Student("Tony Blair")) ;
lect.getAdvisees().add(new Student("Michael Howard")) ;

Note that the association belongs to the Lecturer class (as it is defined in Lecturer's mapping file). This means that adding a student to a lecturer's advisees is considered an operation on a lecturer, not on a student. Thus the SQL statements that would be generated for the above statements would include an insert for the lecturer object, together with an insert each for the two connected student objects (because the students are referred to by the lecturer and we have put the cascade="save-update" declaration in the Lecturer's mapping file. But because the association does not belong to the students, the saving of the students would not set the foreign key value to the advising lecture. Thus there would be two extra update statements for adding the lecturer's foreign key value into the student records. These extra two update statements are not just an efficiency problem: If every student should have a supervisor, then we would like to add the not-null="true" attribute to the key element in the mapping file for the association. However this would cause errors as the above sequence of inserts and updates does insert nulls (if only to immediately update them) where they should never occur.

The solution is to only create such one-to-many associations as the inverse end of a bidirectional many-to-one association. This gives ownership of the association to the Student end and, as we see below, leads to the foreign key being created as part of the initial insert of the Student record instead of after it as a consequence of the Lecturer insert.

No comments:

Post a Comment