Monday, May 3, 2010

Dynamic DAO generation

Almost any developer working on enterprise projects spend reasonable amount of time writing DAO classes and tests for them.
Most of this code is trivial, especially in case of ORM frameworks, and I think that it should be generated instead of hand-writing. Why should I waste my time writing routine code, writing tests for it and leaving space for errors despite of tests?
So I wrote very small framework (not actually framework, just a few classes) that generates DAO classes for Hibernate framework.
You just write interfaces for DAO, annotate them and framework do all the routine work for you.
Code being generated is usually as effective as hand-written one and [most] of bugs are already fixed.

Here how DAO interface looks:
public interface EntityDao extends DynamicDao<Entity, Long> {
        List<SomeEntity> getPaginatedFilteredByCriteriaOrderedByCriteria(
            @Pagination.Paginate Pagination pagination,
            @Restriction.Restrict Restriction restriction,
            @Ordering.Criteria Ordering ordering
    );
}

Here how it can be used:
EntityDao entityDao = (EntityDao) DaoFactory.getInstance(EntityDao.class, Entity.class, sessionFactory);
List<Entity> entities = entityDao.getPaginatedFilteredByCriteriaOrderedByCriteria(
    new Pagination(6, 7), 
    Restriction.or(
        Restriction.ilike("name", "vaLue 1"),
        Restriction.like("name", "value 2")),
    new Ordering("name", Ordering.Direction.DESC).Order("id"));

And this is example of actual code auto-generated for interface method above:
public List getPaginatedFilteredByCriteriaOrderedByCriteria(
   Pagination p0, Restriction p1, Ordering p2) {
    Session session = ThreadLocalSessionFactory.getSession();
    Criteria criteria = session.createCriteria(entityClass);
    Restriction restrictionValue = p1;
    Criterion criterion = getCriterion(restrictionValue);
    if (criterion != null) {
        criteria.add(criterion);
    }
    Ordering sortingCriteriaValue = p2;
    while (sortingCriteriaValue != null) {
        if (sortingCriteriaValue.getDirection() == Ordering.Direction.ASC) {
            criteria.addOrder(Order.asc(sortingCriteriaValue.getField()));
        } else if (sortingCriteriaValue.getDirection() == Ordering.Direction.DESC) {
            criteria.addOrder(Order.desc(sortingCriteriaValue.getField()));
        }
        sortingCriteriaValue = sortingCriteriaValue.getChild();
    }
    if (p0 != null) {
        criteria.setFirstResult(p0.getFirstResult());
        criteria.setMaxResults(p0.getMaxResults());
    }
    return criteria.list();
}

Generated DAO classes can be easily exposed as Spring beans.

More information, docs and examples on project's site on Google Code: http://code.google.com/p/dyndao/

The framework is not finished yet (code is not well documented, there are possibly bugs left etc.) and I have no time for it right now but I hope it will look as a real project in near future.
It looking stable and I know at least one project in production stage that is using it.
Anyone interested in project development are welcome.

2 comments:

  1. Sorry, but seems that you wrote your own Entity Manager with blackjack and pagination )

    And as em, your solution has the same disadvantage: we must copy-paste code of generating query in any place where we want to get the same result. I mean, that queries in more cases are the same, and if we want to get result of crafty query in two or more different places in an application, we must copy-paste code "List entities = entityDao.getPaginatedFilteredByCriteriaOrderedByCriteria( ... etc".

    Seems that i have seen it somewhere before. Oh, yeah, like "List = entitymanager.createQuery("FROM Entity WHERE ... ").setParameter().setFirstResult().setMaxResult()....list()", isn't it?

    So, in other way, if we do not be lazy and write some daoclasses, we can incapsulate this code into the simple dao method with name like "getTop100ParsedCheques()" and use it everywhere. Of course, process of writing dao classes can be simplified by using code templates in IDE and writing GenericDAO, for example like this http://community.jboss.org/wiki/GenericDataAccessObjects

    Please, correct me if I wrong

    ReplyDelete
  2. I don't think that it is replacement for EntityManager with blackjack and whatever.
    I made it as a replacement to IDE code templates. This saves you from writing a few lines of code per DAO method (maybe 20-50 lines) and reduces space for error.

    ReplyDelete