05.31.08

Making Generic DAO “Operation specific”

Posted in Design Patterns, Java at 2:37 am by Administrator

After a long haul in posts, I am back again. Thanks to all of you for you encouraging e-mails, much appreciated. The Generic DAO pattern became a de-facto of sorts in Hibernate / ORM world some time back. In keeping with my never ending quest for improving upon already existing patterns available at hand, let us make our Generic DAO pattern more “specific” in concerns. For those of you who are not familiar with Generic DAO design pattern, refer to: Generic DAO Recently while working on a testing framework @ Home , It became apparent to me that operational testing warrants that we must separate out the functional/operational aspects for a DAO. Quickly recapturing what a Generic DAO does:

  • It is Generically typed to a Model.
  • It concerns itself to a Model and can include ‘n’ number of CRUD or combination of CRUD operations
  • Each Model (known Persistent Class in hibernate world) is a concern of a DAO


Why Generic DAO is not perfect?


For starters a simple justification would be separation of operational concerns; we want our “operational” aspects for that Model / Persistent class be separated. So for example in a DAO if i am doing a SAVE operation, i should only be concerned with doing a SAVE in DAO and nothing else, If i have a DAO method which deletes a model, then DAO should only be able to handle DELETE and nothing else.


Using a Generic DAO can reduce clutter, but to truly separate out operational aspects would make the code even cleaner (off course it adds a “tad” extra burden of writing more classes/interfaces but it also would make the code more robust IMHO), the code needs to be functionally separate as well or operationally separate to make it a better suited candidate for writing robust test cases.

Let’s Get Started!


Continuing with my philosophy of “lets get down and dirty” in code, lets get on with seeing somethings in action. I will start out by creating bunch of interfaces on which I would then elaborate to give an idea as to what I meant when i said “separation of Operational Concerns”. Lets pull out a common Generic DAO interface.

 public interface IGenericOperationDAO<OPERATION extends IOperation<TYPE>,
                                           TYPE extends Serializable,
                                          MODEL extends AbstractModel<>,
                                          ID extends Serializable>{

	/**
	 * Performs operation on a model.
	 *
	 * @param operationType
	 *            operation to perform
	 * @param model
	 *            model to be operated upon.
	 * @see IOperation
	 */
	public abstract void performOperation(final OPERATION operationType,
			final MODEL model);
}

Lets examine this interface a little to get a glimpse of what i am talking about. IGenericOperationDAO is tied to a MODEL (an AbstractModel is just an abstract class that extends Serializable and serves as a common model for our Models that match the tables in Database). Our GenericOperationDAO interface has one method, that is well apparently very "Generic", it just says performOperation takes in an IOperation type and a model. an IOperation type interface is basically something like this:

   	/**
	 * Gets the operation type.
	 *
	 * @return the operationType
	 */
	public abstract TYPE getOperationType();

An operation type can be DELETE, CREATE, READ, UPDATE or anything of that sort. An implementation for this operation is as follows. (For simplicity’s sake, lets assume that we need to do only a deletion (C-U-D operations will be the same).:

public class DeleteOperation implements IOperation<String> {

	/**
	 * {@inheritDoc}
	 */
	public final String getOperationType() {
		return "Delete";
	}

}

As you can make out that operation type simply returns a delete “string” (this can be useful in comparing operation types within a DAO), a string is not very robust approach IMHO, if someone has better ideas, feel free to share :) !

Now since perform operation is perhaps too generic, lets extend this interface to incorporate our deletions. Here is an example of a generic Delete interface:

public interface IGenericDeleteOperationDAO extends IGenericOperationDAO<DeleteOperation, String, AbstractModel<Long>, Long> {

	/**
	 * Deletes a model.
	 *
	 * @param model
	 *            model type to be deleted.
	 */
	public abstract void processDelete(final AbstractModel<Long> model);
}

Notice that our generic delete DAO is generically typed to DeleteOperation type.
As we can make out from the above, the generic delete dao inherits performOperation from its generic counter part and extends it by adding processDelete which basically takes in the model and deletes it. All implementing classes (Concrete that is) are expected to implement this method. But as we can make out that we probably don’t want to do certain kinds of things that are in here by implementing this interface directly. Implementing this interface has to be discouraged here since it does NOT directly enforce me going and implementing performOperation unfortunately. To avoid that from happening, lets create another abstraction (i.e. an Abstract Class which implements certain parts of this functionality), see below:


// java docs left out for clarity
public abstract class AbstractGenericDeleteOperationDAO implements IGenericDeleteOperationDAO {

	/**
	 * Default constructor. Calls perform
         * operation which in turn calls process
         * delete to delete the model given.
	 */
	public AbstractGenericDeleteOperationDAO(final DeleteOperation operationType, final AbstractModel<Long< model) {
		performOperation(operationType, model);
	}

	// javadocs left out
	public abstract void processDelete(final AbstractModel<Long< model);

	//javadocs left out
	public void performOperation(DeleteOperation operationType, AbstractModel<Long< model) {
		if (!operationType.getOperationType().equalsIgnoreCase("delete")) {
			throw new UnsupportedOperationException("No other operation except DELETE supported");
		} else {
			processDelete(model);
		}
	}

}

Quickly the above code will do this:

  • Constructor calls the default implementation of perform operation to account for any other operations if any being passed in, for example if a user manages somehow to pass in something other than a Delete Operation
  • calls in process delete which is an abstract method, all extending classes (Concrete) will be Required to implement this method (if they are a Delete DAO type)

So basically a simple implementation of a DAO from a client perspective would look like this:

/**
 * Employee delete dao. Concerns itself with just the delete operations for an Employee Model.
 *
 * @author Anirudh Vyas
 */
public class EmployeeDeleteOperationDAO extends AbstractGenericDeleteOperationDAO {

	/**
	 * Full constructor.
	 *
	 * @param operationType
	 *            delete operation type.
	 * @param model
	 *            model type to be operated upon.
	 */
	public EmployeeDeleteOperationDAO(DeleteOperation operationType, AbstractModel<Long> model) {
		super(operationType, model);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final void processDelete(final AbstractModel<Long> model) {
		// perform Hibernate Deletes here...
	}

}

Now at real client side for this DAO would be a simple matter of doing something like new EmployeeDeleteOperationDAO(DELETE, employeeModel); and viola! we are done! … similar pattern can be followed for Creation, Reads, Updates. For combination of CRUD operation, a perform operation should be enough. Later we will see how we can improve upon the operation type to make it a little more dynamic than just comparing it with a string or returning a simple string “delete” or a “create” in operation type, so stay tuned and thanks for reading!

Addendum : understandably there can be improvements done in this; refinements etc. If you have any feel free to comment out here, instead of mailing them. That would keep it consistently here. I will try my best to post whenever i get any emails about the refinement.

Regards
Vyas, Anirudh

Leave a Comment