Sunday, July 16, 2006

DAOs on Steroids - Fun Unlimited with Generic DAOs in Spring 2.0

I have already blogged a lot on DAOs in general and generic DAOs in particular (see here, here and here). All the entries are enough to prove that generic DAOs provide better engineering than boilerplate DAOs, resulting in a substantial lesser amount of code. This entry rounds up all of my thoughts on generic DAOs and how their deployment with the Spring 2.0 container results in a seamless injection into the domain model of an application. Without much ado, buckle up for the fun-unlimited in the roller coaster ride of the DAO world.

Generic DAO Abstraction

At the risk of repetition, let me recall the generic abstraction for a DAO - the DAO is parameterized on the class DomainBase, the base class for all domain objects. In case u want to use DTOs, the parameterization can also be done on DTOBase as well. Currently let us assume that we will use the rich domain model without the behaviourless DTOs.

public abstract class DAOBase<T extends DomainBase> {
  // The underlying implementation.
  private DAOImplBase<T> daoImpl;
  ...
}


The above abstraction uses the pimpl idiom and the Bridge pattern to decouple the abstraction from the implementation. The implementation can be based on JDBC, Hibernate or JPA and can be switched flexibly, without an iota of impact on the client codebase. Cool!

The concrete DAO extends the abstract DAO :

public class EmployeeDAO<T extends DomainBase> extends DAOBase<T> {
  ...
}


The concrete DAO can be kept generic as well, in order to allow the user to instantiate the generic DAO with multiple domain abstractions. If u want to keep it simple, you can make the concrete DAO a non-generic implementation as well ..

public class EmployeeDAO extends DAOBase<Employee> {
  ...
}


The Bridge

The abstraction DAOBase<T> delegates all DAO methods to the implementation, which is concretized based on the implementation platform - Hibernate, JPA, or vanilla JDBC.

public abstract class DAOBase<T extends DomainBase> {
  ...
  // sample finder
  public final <Context> List<T> find(
        Context ctx,
        ICriteria cri,
        Class<T> clazz) {

    return daoImpl.read(ctx,
      getTableName(),
      AndCriteria.getInstance(
      getJoinCondition(),
      cri),
      clazz);
  }
  ...
}


The implementation leg of the bridge gives concrete implementation for every specific platform ..

// JDBC based implementation
public class DAOJDBCImpl<T extends DomainBase>
    extends DAOImplBase<T> {
  ...

  @Override
  public <Context> List<T> find(Context ctx,
        String tableName,
        ICriteria cri,
        Class<T> clazz) {

    try {
    Connection conn = (Connection) ctx;

    SelectStatement stmt = new SelectStatement()
        .setFromClause(tableName)
        .setSelectClause(" * ")
        .setWhereClause(cri);

    List<T> result =
      QueryBuilderUtils.query(conn, stmt.toSelectString(), clazz, 0);
    return result;
    } catch (SQLException e) {
      throw new DataAccessException(e);
    }
  }
  ...
}


Similarly for JPA based implementation ..

// JPA based implementation
public class DAOJPAImpl<T extends DomainBase>
      extends DAOImplBase<T> {
  ...
}


Concrete DAOs

Now that the base abstraction DAOBase provides the contract and the implementation hierarchy provides generic implementation for all the DAO methods, the concrete DAOs only have to provide the table specific information that will be used by the typed interfaces during runtime.

Here's a minimalist implementation ..



public class EmployeeDAO<T extends DTOBase>
      extends DAOBase<T> {

  public enum ColumnNames {
    // Enum for column names.
  }

  /**
   * Returns a list of the column names.
   * @return list of column names.
   */
  protected List<String> getColumnNames() {
    ...
  }

  /**
   * Subclasses must override and provide the TABLE_NAME
   * that the bean is associated with.
   *
   * @return the table name.
   */
  public String getTableName() {
    return "EMPLOYEE";
  }

  /**
   * {@inheritDoc}.
   */
  protected ICriteria getPrimaryKeyWhereClause(T employee) {
    ...
  }
}



Deploy in Spring

The creation of DAOs can be controlled through the Spring IoC - all DAOs will be singleton beans, lazily loaded ..

<bean id="empdao"
  class="org.dg.gen.EmployeeDAO"
  lazy-init="true"
</bean>


Quite straightforward - uh!

In case of generic concreate DAOs, the EmployeeDAO<Employee> will have to be instantiated through a static factory method ..

public class EmployeeDAO<T extends DomainBase> extends DAOBase<T> {
  ...
  public static EmployeeDAO<Employee> makeDAO() {
    return new EmployeeDAO<Employee>();
  }
}


No problem .. add the factory method in configuration and the Spring managed bean lookup-method-injection magic takes care of the rest.

<bean id="empdao"
  class="org.dg.gen.EmployeeDAO"
  lazy-init="true"
  factory-method="makeDAO">
</bean>


The DAO needs to be wired with the domain object, since the rich domain object may need to query the database using the DAO. Spring 2.0 to the rescue - Spruce Up Your Domain Model and inject the DAO within it through 2.0 magic ..

@Configurable
public class Employee extends DomainBase {

  private EmployeeDAO<Employee> dao;

  public void setDao(EmployeeDAO<Employee> dao) {
    this.dao = dao;
  }
  ...
}


Remember the Employee domain object will not be instantiated by Spring - yet the setter injection works, courtesy the @Configurable annotation. And of course the following addition in the configuration ..

<aop:spring-configured/>
<bean class="org.dg.gen.Employee"
  singleton="false">
  <property name="dao"><ref bean="empdao"/></property>
</bean>


This is Spring 2.0 dessert course - more elaborated here.

Excitement with AOP Introductions

The Spring AOP Introductions magic helps u to add functionality to an existing DAO by wrapping it in a proxy and defining new interfaces to be implemented. This article describes how introductions are used to add a number of custom finder methods to DAO implementations specific to each domain object. This is a real treat that Spring AOP offers to customize your DAOs individually on top of generic abstractions. The custom functionality can be implemented as separate classes / interfaces and injected specifically to selected DAOs as u need 'em. The best part is that all of this machinery works non-invasively - your individual concrete DAOs can still be generated through your MOJOs (as maven plugins), yet you can add specific functionality of custom interfaces injected through the magic of Spring AOP.

As the DeveloperWorks paper suggests, all u have to do to integrate this AOP machinery into ur application's configuration file :


  1. Define a bean for custom org.springframework.aop.IntroductionAdvisor for handling additional methods that u would like to introduce to ur DAO

  2. Define a bean for the target of interception, which the proxy will wrap. Make it abstract to enable reuse of the definition in specific DAOs

  3. Define the proxy of the class org.springframework.aop.framework.ProxyFactoryBean, which will wrap the target of above step

  4. Finally add the bean for the specific DAO along with the proxy interfaces that it needs to implement



For the full and final elaboration, have a look at the excellent treatment of this subject in the paper mentioned above.

No comments: