Firestorm/DAO from CodeFutures is one of the very popular products in the market today that generates all DAOs and POJOs once you feed in your schema for the database. The code generator from Firestorm/DAO generates one DAO interface and one implementation class for each database table along with separate classes for the DTOs, factories and the exceptions. Looking at the generated code, I somehow have the feeling that they could have been better engineered through employing a higher level of abstraction. For every DAO, the basic functionalities provided are the same; e.g.
findByPrimaryKey()
locates a record from the table using the passed in primary key; findAll()
fetches all records from the table - yet we do not find any base class abstracting the commonality of SQL processing! In the meantime, I have been doing some work outs on generic DAOs, with the idea of making the generated code more compact and better engineered without sacrificing an iota of typesafety. My last posting on this subject has some information on what I thought could be the base of my design - a Bridge based implementation of generic DAOs -
- the interface hierarchy providing the contracts to be extended by application DAOs
- the implementation hierarchy, providing options to implement the DAOs using different engines like JDBC, Hibernate etc.
Here's a sample usage for a table Employee for executing a query with a dynamic criterion :
// make the DAO
EmployeeDAO<Employee> e =
DAOFactory.getInstance().getEmployeeDAO();
// make the criteria
ICriteria cri = new SimpleCriteria("employee_id = 10");
// get 'em
List<Employee> es = e.read(cri, Employee.class);
Note that in the above snippet,
EmployeeDAO
is a generic class and takes a POJO as the actual parameter of instantiation. EmployeeDAO
has been generated as :public class EmployeeDAO<T extends DTOBase> extends DAOBase<T> {
....
}
An alternative could have been to have the
EmployeeDAO
as a concrete class with the POJO Employee hardwired :public class EmployeeDAO extends DAOBase<Employee> {
....
}
Well, it really depends upon how much flexibility you would want to give to your users. With the former approach, the user is allowed to construct the
EmployeeDAO
with any other POJO, so long the POJO contains the properties that match the database column names. This is often useful when the user wants to work with database views in the application, which has different backing POJOs than the ones associated with the main tables.The abstraction
DTOBase
provides a suitable container for dumping all stuff common to all DTOs, e.g. bean state management functionalities (whether it has been changed since its last fetch from database) are very powerful candidates for this placeholder. Incidentally these functionalities are scattered throughout all POJOs in Firestorm/DAO generated codes.Projection on a Table is also a DAO!
With the above code snippet for query, we can read entire records from the Employee table to the List<Employee>. Now what about projections - I want to select a few columns of the table into my POJO. The point to note is that the end result of a Projection is no different than the above
read()
, except the fact that we now have a smaller list of columns to deal with. It's no different from an operation on a DAO, which transforms the end result to contain a subset of the column list. Think Decorator! Decorate a DAO to get the Projection :
public class Projection<T extends DTOBase> extends DAOBase<T> {
private DAOBase<T> base;
...
}
And now the usage :
// set up the Projection
Projection<Employee> ep =
DAOFactory.getInstance()
.getEmployeeProjection()
.setColumn(EmployeeDAO.ColumnNames.EMPLOYEE_ID)
.setColumn(EmployeeDAO.ColumnNames.EMPLOYEE_NAME);
// get 'em
List<Employee> es = ep.read(cri, Employee.class);
Cool stuff ! We have the encapsulation of the SQL married to the typesafety of operations. The user does not have to write SQL statements or use hardcoded column names that can be checked only during runtime. That was the promise of the DAO Design Pattern - right ?
How about Joins ?
Joins are nothing but compositions of multiple tables based on primary key / foreign relationship. The result of a Join is also a DAO!
public class Join<T extends DTOBase> extends DAOBase<R> {
public Join<T> addJoinParticipant(DAOBase<? extends DTOBase> dao){
...
}
}
Compose as you Wish
// make the DAO
EmployeeDAO<Employee> e =
DAOFactory.getInstance().getEmployeeDAO();
// make the DAO
EmpSalaryDAO<EmpSalary> s =
DAOFactory.getInstance().getEmpSalaryDAO();
// make the criteria
ICriteria cri = new SimpleCriteria(
new StringBuilder(128)
.append(EmployeeDAO.ColumnNames.EMPLOYEE_ID.name())
.append(" = 10").toString());
Join<EmpSalaryDetails> d =
DAOFactory.getInstance()
.getEmpSalaryDetailsDAO()
.addJoinParticipant(e)
.addJoinParticipant(s)
.addLeftColumn(EmployeeDAO.ColumnNames.EMPLOYEE_ID)
.addRightColumn(EmpSalaryDAO.ColumnNames.EMPLOYEE_ID);
Projection<EmpSalaryDetails> esd =
new Projection<EmpSalaryDetails>(
new DAOJDBCImpl<EmpSalaryDetails>(),
d);
// get 'em
List<EmpSalaryDetails> es = esd.read(cri, EmpSalaryDetails.class);
3 comments:
Very interesting. I mostly use Hibernate for my ORM and Spring as the wiring, so its useful to have a generic base Dao class, with methods that basically delegate to the underlying HibernateTemplate object, so I was familiar with the idea of a generic base Dao class. Something like this:
BaseDao<T> {
get(Class clazz, Serializable id) : T {
return getHibernateTemplate().get(clazz, id);
}
...
}
Your ICriteria implementation is very similar to the Hibernate Criteria implementation, so I guess you are on to something here (great minds think alike) :-).
One thing I have never thought of before is how you extended the Dao concept to include Projections and Joins. And yes, when you think about it, it makes so much sense.
Keep up the great work!
Are you still using this idiom? If so would you be willing to share the code? I'm interested in creating a generic dao framework that could be used with any engine ie Hibernate, Torque, etc. And the main point of interest for me right now if the joining of multiple tables.
@Daniel: I was mainly using this idiom for native JDBC based DAOs. But I find that u r using Hibernate as the ORM framework. Do u still want to use joins in SQLs ? I think having a rich domain model and ORM based associations will be a better approach. And Hibernate offers lots of features on this. A very useful pattern to use in this modeling is the Repository pattern of Eric Evans as mentioned in his DDD book. Sometime back I had blogged on using the Repository instead of the DAOs as a domain level abstraction. Have a look at these posts - I am sure u will find them useful. They talk about generic Repository design instead of generic DAO design. Also have a look at Chris Richardson's blog entry on generic DAOs. He offers a very elegant solution using Spring.
Regarding the codebase, I have to dig it out, since it has been quite some time since I wrote them. But I would strongly recommend u to use domain model associations and hibernate mapping leaving the joins to the underlying ORM engine. Mine was one meant to be used for native JDBC based DAO abstractions.
Cheers.
Post a Comment