Future-Proofing Java Data Access - DAO Pattern Done Right

nvisia is an award-winning software development partner driving competitive edge for clients.

Introduction

When it comes to writing software at an enterprise level, most established developers are familiar with a variety of different approaches to accessing data.  In the most common case, a database houses the data that needs to be accessed. Different techniques such as result sets and object-relational mappings have typically provided effective ways to approach data access.  With all of these different techniques, should a developers approach for exposing methods to access data change if the data source is not a database; such as a web service or a product API?  The answer is NO; it should not matter what type of data source is being accessed if the DAO pattern is being used properly.

The Pattern

A data access object (DAO) is an implementation of an interface which accesses some type of database or other persistent mechanism.  DAO objects sit in the persistence layer of an application and are typically accessed in memory.  Developers have used a variety of different approaches towards implementing DAOs.  Which way is the right way?  It’s time to review the pattern, understand the intent and make sure the pattern is being done correctly.

As a traditional core J2EE pattern, the DAO pattern structure is represented in the following class diagram:

 Coller_Diagram_1 

The thing to point out is a DataAccessObject obtains/modifies a TransferObject (DTO, a related core J2EE pattern).  A TransferObject is an object which application clients use to exchange data.  These objects define the data which our applications and services exchange.  The transfer objects should be simple POJOs (Plain Old Java Objects). In the MVC world these are sometimes called ViewModels and are used to render an application view.

The intent of the DAO pattern is to simply allow access to the underlying persistent store’s data while encapsulating the type of datasource store from the consumers.  This intent as a whole can become misconstrued, especially when using various data mapping techniques (such as ORM, object-relational-mapping or WSDL-2-code generators).  To better explain this, here is an example.

Example:

[https://github.com/thedevolution/dao-pattern]

 Here is a simple DAO interface:

public interface PersonDAO {

      Person findById(Long id);

      Long save(Person person);

}

 

The Person data transfer object should be a simple POJO:

public class Person implements Serializable {

 

      private static final long serialVersionUID = 1L;

     

      private Long identifier;

      private String firstName;

      private String lastName;

      private String middleInitial;

      private Date dateOfBirth;

     

      // Getters and Setters…

}

Both the DAO interface and the transfer objects give no idea to the consumer what type of persistent mechanism is being used.  This achieves encapsulation, thus using the pattern correctly.

 

If you Google “generic dao pattern”, you’ll see many examples of how DAOs do not follow the patterns intent. To demonstrate, here’s a common “generic dao pattern” abstract base-class for JPA DAO implementations:

public abstract class BaseJpaDAO<E, PK extends Serializable> {

     

      private Class<E> classToBePersisted;

      private EntityManager entityManager;

 

      protected BaseJpaDAO() {

            Type type = getClass().getGenericSuperclass();

            if (type instanceof ParameterizedType) {

                  setParameterizedType((ParameterizedType) type);

            } else {

                  type = getClass().getSuperclass().getGenericSuperclass();

                  if (type instanceof ParameterizedType) {

                        setParameterizedType((ParameterizedType) type);

                  }

            }

      }

     

      @SuppressWarnings("unchecked")

      private void setParameterizedType(ParameterizedType parameterizedType) {

            this.classToBePersisted = (Class<E>) parameterizedType

                        .getActualTypeArguments()[0];

      }

     

      @Transactional(readOnly = true)

      public E findById(PK id) {

            return getEntityManager().find(classToBePersisted, id);

      }

     

      @Transactional

      public final PK save(E entity) {

            getEntityManager().persist(entity);

            return extractPrimaryKey(entity);

      }

}

 

While this would be effective and easy for implementing the PersonJpaDAO, the above example breaks the DAO pattern by exposing the JPA entity objects via the DAO interface. The Person object defined on the PersonDAO interface would have to be converted from a simple data transfer object to an entity containing JPA annotations.  This is evident by the save method asking for an entity object.  Now the persistent mechanism is exposed to the DAO consumer.  This is bad, especially if the persistent mechanism someday needs to be switched to something other than a database.  If the pattern is done right, the Person object stays as a simple DTO and gets translated within the DAO implementation to/from the specific persistent mechanism.

Here is a modification to the base JPA DAO class that allows it to adhere to the DAO pattern and achieve encapsulation:

public abstract class BaseJpaDAO <E, PK extends Serializable, TO> {

 

      private Class<E> classToBePersisted;

      private EntityManager entityManager;

 

      protected BaseJpaDAO() {

            Type type = getClass().getGenericSuperclass();

            if (type instanceof ParameterizedType) {

                  setParameterizedType((ParameterizedType) type);

            } else {

                  type = getClass().getSuperclass().getGenericSuperclass();

                  if (type instanceof ParameterizedType) {

                        setParameterizedType((ParameterizedType) type);

                  }

            }

      }

     

      @SuppressWarnings("unchecked")

      private void setParameterizedType(ParameterizedType parameterizedType) {

            this.classToBePersisted = (Class<E>) parameterizedType

                        .getActualTypeArguments()[0];

      }

     

      @Transactional(readOnly = true)

      public TO findById(PK id) {

            return assemble(getEntityManager().find(classToBePersisted, id));

      }

     

      @Transactional

      public final PK save(TO transferObject) {

            final E toPersist = disassemble(transferObject);

            getEntityManager().persist(toPersist);

            return extractPrimaryKey(toPersist);

      }

 

      protected abstract PK extractPrimaryKey(E entity);

     

      protected abstract E disassemble(TO transferObject);

     

      protected abstract TO assemble(E entity);

}

 

 Extending this new base class and implementing the PersonDAO interface yields the following: 

public class PersonJpaDAO extends BaseJpaDAO<PersonEntity, Long, Person> implements PersonDAO {

 

      @Override

      protected Long extractPrimaryKey(PersonEntity entity) {

            return entity.getIdentifier();

      }

 

      @Override

      protected PersonEntity disassemble(Person transferObject) {

            PersonEntity toReturn = null;

            if (transferObject != null) {

                  toReturn = new PersonEntity();

                  toReturn.setFirstName(transferObject.getFirstName());

                  toReturn.setLastName(transferObject.getLastName());

                  toReturn.setMiddleInitial(transferObject.getMiddleInitial());

                  toReturn.setIdentifier(transferObject.getIdentifier());

                  toReturn.setDateOfBirth(transferObject.getDateOfBirth());

                 

            }

            return toReturn;

      }

 

      @Override

      protected Person assemble(PersonEntity entity) {

            Person toReturn = null;

            if (entity != null) {

                  toReturn = new Person();

                  toReturn.setFirstName(entity.getFirstName());

                  toReturn.setLastName(entity.getLastName());

                  toReturn.setMiddleInitial(entity.getMiddleInitial());

                  toReturn.setIdentifier(entity.getIdentifier());

                  toReturn.setDateOfBirth(entity.getDateOfBirth());

            }

            return toReturn;

      }

}

 

Notice the transfer object (TO) is exposed via the save and findById methods declared in the PersonDAO interface. The PersonEntity JPA object is hidden/encapsulated within the DAO implementation.  The consuming code does not have any idea what the true persistence mechanism is. If the persistence mechanism needed to be switched to a web service, ERP API or other master-data source, only the underlying DAO implementation would have to change.  All consuming code would be none the wiser. This is where the pattern is really useful.

Additional Considerations

The above example does not directly take exception shielding into consideration.  Exception shielding is the process of properly translating a specific type of exception to a more neutral exception type.  Spring does a good job of handling this by allowing post processors to be declared which can translate persistent exceptions to general data access exceptions. These data access exceptions are independent of the underlying persistent mechanism.  This will be addressed in a future article.

Conclusion

By understanding the intent of the DAO pattern, a developer will be able to decide if they want to build better, more scalable data access code. They can now make an architectural decision on whether to follow the pattern or instead take a short-cut to make things easier on other developers.  The choice is theirs.  In conclusion, this article shows why encapsulating the persistent mechanism from consuming code is beneficial in a world where the data store landscape and integration strategies are rapidly evolving.

Source code available at: https://github.com/thedevolution/dao-pattern

Related Articles