Using the Java Persistence API with Spring 2.0

by Seth White
03/20/2006

MedRec data access

The Medical Records application uses four different DAOs, one for each of the domain model classes— Patient, Prescription, Record, and User—that it needs to persist. The DAO implementation classes are named JpaPatientDAO, JpaPrescriptionDAO, JpaRecordDAO, and JpaUserDAO, respectively, and can be found in the com.bea.medrec.dao.orm package.

Let's take a closer look at the Patient DAO to get an idea of how things work. Here is the interface of the data access object that is used to handle retrieval and update of patient data in the medical records application:


public interface PatientDao {



  public Patient getById(Integer patientId) 

    throws DataAccessException;



  public List getByEmail(String email) 

    throws DataAccessException;



  public List getByLastName(String lastName) 

    throws DataAccessException;



  public List getByLastNameFirstName(String lastName,

    String firstName) throws DataAccessException;



  public List getByLastNameWild(String lastName) 

    throws DataAccessException;



  public Patient getBySsn(String ssn) 

    throws DataAccessException;



  public List getByStatus(String status) 

    throws DataAccessException;



  public Patient getByUsername(String username) 

    throws DataAccessException;



  public Patient save(Patient patient) 

    throws DataAccessException;

  

  public Patient update(Patient patient) 

    throws DataAccessException;

}

Notice that the Patient DAO interface is a plain old Java interface (POJI). It doesn't extend or import any Java types that are specific to the persistence technology being used—JPA in this case. Most of the Patient DAO methods perform a query that returns either a list of patients or a single patient object. The final two methods are used to save a new patient or update an existing patient's information in the database, respectively.

Each method on the interface declares a DataAccessException, which is a runtime exception defined by the Spring framework. There are two things to note about DataAccessException. First, it is a runtime exception, so application code that uses the data access object is not required to wrap each invocation with a try-catch block, as is the case in JDBC and EJB 2.x entity beans. Second, DataAccessException is useful because it wraps the specific exception classes that are used by the underlying persistence technology and thereby keeps the rest of the application independent of the persistence layer.

So much for the interface. Next, let's take a look at the implementation of the Patient DAO. Here is the base class, BaseDAO, that all data access objects in MedRec extend:


package com.bea.medrec.dao.orm;



import org.springframework.orm.jpa.support.JpaDaoSupport; 



public abstract class BaseDao extends JpaDaoSupport {

  ...

}

This is a very simple class in our case, but in general it could be used for any code that is common across different data access object implementations. BaseDAO extends Spring's JpaDaoSupport class. This is a must, as it gives our DAO implementation classes access to Spring's JPA-specific APIs.

The Patient DAO implementation class, JpaPatientDao, extends BaseDao:


public class JpaPatientDao extends BaseDao implements PatientDao {



   public Patient getById(Integer pId) 

      throws DataAccessException {

      return getJpaTemplate().find(Patient.class, pId);

    }



    public List getByLastName(String pLastName) 

      throws DataAccessException {

        

        List patients = getJpaTemplate().find(

            "SELECT p " + 

            "FROM " + Patient.class.getSimpleName() + " p " + 

            "WHERE p.lastName LIKE ?1", pLastName);



         return (patients.isEmpty())? 

            Collections.EMPTY_LIST : patients;

    }

    ...

}

That code sample also shows two exemplary query method implementations. The first, getById(), looks up a patient by its unique identifier field. This is done by calling a special find method on Spring's JpaTemplate, which takes the Patient class and a unique id as parameters and returns the Patient object that has that id. The getByLastName() method returns a list of Patients that have a similar last name. getByLastName() uses an alternative version of JpaTemplate.find() that takes as parameters an EJB QL query followed by the query parameters and returns a list of objects that match the query. Here are the patient DAO save and update methods:


public class JpaPatientDao extends BaseDao implements PatientDao {



   ...



   public Patient save(Patient pPatient) 

      throws DataAccessException {

      getJpaTemplate().persist(pPatient);

        

   return pPatient;

   }



   public Patient update(Patient pPatient) 

      throws DataAccessException {



      return getJpaTemplate().merge(pPatient);

   }

}

In this implementation of the Patient DAO save and update methods, the save() method is used to insert a new patient record into the database, while update() modifies an existing patient record. Each method returns a reference to the new or updated Patient object. It's important to understand that persist and merge don't immediately result in a database update. Changes are usually cached by JPA until the current transaction commits. Then the changes are sent to the database as a batch, but pending changes can also be flushed more often, whenever a query is run.

It's interesting to note that there is very little dependence on any JPA APIs in our Patient DAO object since the Spring template is used for all data access operations and it delegates to JPA internally. Indeed, the only direct dependency on JPA that we have seen so far has been the EJB QL query string used by the getByLastName() method. Also note that although earlier we referred to Spring DAOs as POJOs, this isn't entirely true since DAOs are required to extend Spring's JpaDaoSupport class. What is important, however, is that the interface DAOs expose to the rest of the application is a plain old Java interface that doesn't depend on JPA or Spring types.