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 :
- Define a bean for custom
org.springframework.aop.IntroductionAdvisor
for handling additional methods that u would like to introduce to ur DAO - 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
- Define the proxy of the class
org.springframework.aop.framework.ProxyFactoryBean
, which will wrap the target of above step - 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:
Post a Comment