Using the Java Persistence API with Spring 2.0

by Seth White
03/20/2006

Relationships

Most entities have relationships to some other entity or entities in the domain model, and the Patient class is no exception. Figure 2 shows a diagram of the Patient class and its relationships.

Figure 2
Figure 2. Patient class relationships

JPA uses the OneToOne, ManyToOne, OneToMany, and ManyToMany annotations to specify relationships and their cardinality. Every relationship field must be annotated with one of these annotations. Patient has a ManyToOne relationship with Address, for example. This means that many patients may have the same address. Patient has a OneToOne relationship with the User class, which contains the patient's username and password information. This means that each Patient is related to a single User.

Patient has a OneToMany relationship with both Prescriptions and Records since a particular patient can have more than one prescription and may have made more than one visit to a doctor (which results in a medical record being created). Conversely, each Prescription or Record can be related to only one Patient.

Cascade operations

An interesting feature of JPA relationships is cascading of operations. You may already be familiar with the "cascade delete" feature that most relational databases support. JPA takes the idea of cascading an operation like delete and applies it to other operations as well, such as insert and update. The Patient class cascades insert and update operations to its related Address and User. Insert is referred to as persist in JPA since a database insert is performed when an object is made persistent. Update is referred to as merge since an update merges the changes in an object into the current JPA persistence context. Don't worry if this seems confusing; just remember to call merge when you want to update an object that is already persistent and to call persist to insert a new persistent object in the database.

Deciding whether or not to cascade a particular operation, such as persist, merge, or remove, can be tricky. In the case of the Patient class, persist is cascaded to Address and User because the application also needs to insert a new Address and User whenever a new Patient is inserted. Similarly, the Address and User may also need to be updated when a Patient is updated. Prescriptions and Records work differently. The application has operations that explicitly persist (and update) a Prescription or Record, so there is no need to cascade these operations from Patient to these classes.

You may remember that in a previous section of this article we said that multiple Patients are allowed to refer to the same Address—the relationship from Patient to Address is ManyToOne. Yet, we also just said that Patient cascades inserts to Address, which results in the creation of a new address for each patient in the database. So, what gives? Well, what you see here is an interesting effect of using generated primary keys and cascade insert between Patient and Address. Because the primary keys for address are generated automatically by the database, every Address will have a unique primary key, and it's possible for two different Address objects that refer to the same real address to be inserted in the database. This is fine for many applications, including MedRec, but if you want to prevent duplicate addresses such as this, your DAO needs to perform some additional logic.

Web Services Considerations

The fact that MedRec's entity classes are mapped both to a database schema by JPA and to XML by the WebLogic Server 9.1 Web services implementation raised some additional issues that affected the design of the entity classes.

JPA supports both unidirectional and bidirectional relationships, but if you look at the entity classes in MedRec you will see that all the relationships are unidirectional. This was necessary because the Web services binding could not support bidirectional relationships.

Another problem arose because of JPA's requirement that Java Collection types, like java.util.Set, be used for multivalued relationship fields. It turns out that the Web services implementation in 9.1 doesn't support Collection types. So, how did we get around this?

Recall that the object-relational mapping can either be defined on the fields or on the methods of an entity. This flexibility on the part of JPA turned out to be very important. Web services, on the other hand, use the public property accessor methods ( getXXX() methods) of a class to define the properties that are marshaled into XML. By defining the persistence mapping using fields and letting the XML binding be defined by the methods on the class, we were able to keep the two mappings separate! Multivalued relationships used a Set internally for persistence but were exposed via the property access methods as an array that the XML binding could handle.

To make this concrete, here is an example of a property access method from the Record class that performed the translation:


public Prescription[] getPrescriptions() {

   if (prescriptions == null)

      return null;



   return (Prescription[]) prescriptions.toArray(new 

      Prescription[prescriptions.size()]);

}

Internally, the method accesses the persistent prescriptions field which has type java.util.Set. The Set is translated into an array that the Web services implementation can handle. Our experiences with Web services integration left us very impressed with JPA and its ability to integrate with an existing application and infrastructure.

This wraps up the discussion of persistent classes. It may seem like you need to follow a lot of rules and restrictions, but in practice most of the JPA rules simply embody good coding practice and you will probably find that you are following them without giving it a lot of thought.