This tutorial introduces compound primary keys and map relations. Both were initially supported in Resin-CMP 1.0.4. Applications can take advantage of these more advanced CMP features to more cleanly model database tables.
Compound primary keys are used for beans with two or more fields as the primary key. A compound primary key is implemented by creating a special key class containing the fields.
Map relations are allowed for entity beans with a primary key pair where one of the primary keys is an identifying relation. The child bean's identifying relation implicitly selects the parent bean and the other key is used as the Map relation's key field.
An identifying relation with a two-field compound primary key can support a map relation. In this example, each Student has a Grade for each Course he takes. The Grade bean has two primary keys: the Student and the Course. The two keys are identifying relations since they refer directly to the Student and Grade objects, not indirectly through their keys.
Part of the the primary key class looks like:
The read-only getGrades map relation belongs to the Student bean. The map's key is the Course object (not the key for the Course.) Resin-CMP will use the Student object as thefield of the composite key, and it will use a Course argument to the Map.get(course) call as the other key. Since a Student and a Course uniquely identify a Grade, Resin-CMP can use the combined key to look up the Grade object.
The map relation is read-only since its data comes from the Grade table. In other words, it makes no sense to call getGrades().put(...). Since Grades beans are created with ejbCreate and destroyed with ejbRemove, it doesn't make sense for the Map to be writable.
The Map is a live object. In other words, when a new Grade is created or an old one is destroyed, the Map object is automatically updated. Of course, the Map needs to be used in the same transaction as the getGrades() call.
The database has tables for Students, Courses, and Grades. The Grade table is dependent on both the Students and the Courses. Because of this dependency, Resin-CMP will automatically delete a student's grades when the student is removed.
The Student's map relation is implemented with one main SELECT query for the keySet(). The get() method is implemented by calling GuestHome's findByPrimaryKey method. The keySet() returns a set of Course beans and the query looks like:
The get(Object) method takes a Course argument and returns a Grade object. The query looks like:
The example client servlet uses the Map like any other java.util.Map. The servlet iterates over all the students and displays their grades. The Courses are found using the getGrades().keySet() iterator and then the Grade itself is found using the getGrades().get(Object) map.
A compound primary key is made up of a combination of persistent fields or identifying relations. This example uses two identifying relations: Student and Course. Only the home interface is different for a compound primary key bean. The local interface and the bean implementation are just like single key beans.
Compound primary keys may have any number of fields. The special case of two fields with at least on being an identifying relation allows for java.util.Map collections in the parent map, like the getGrades() method in the Student bean. The identifying key corresponding to the bean, e.g. Student, is used to select the grades. The second key, in this case Course, is used as the keySet(). The bean itself is the value.
The bean key is a public class with public fields for each key component. In this case, the GradeKey class has both a public field for the student and the Course.
The compound key class must override the equals and the hashCode methods. If all the component keys are identical, the keys must be identical, even if they are separate Java objects. Resin-CMP needs both hashCode and equals for the bean's caching.
Many database entities need to store named attributes, e.g. a Student needs to store her Grades, indexed by the Course she took. By implementing this common pattern directly with the standard Map API, and avoiding working directly with JDBC, applications using Resin-CMP can create cleaner Java code, avoid common maintenance issues during development, and take automatic advantage of database caching.