Introduction to DAO

When working with a database through JDBC or even through Hibernate, the code often turns out to be more cumbersome than we would like. A database query often contains:

  • data validation
  • setting request parameters
  • HQL query selection depending on query parameters
  • constructing a query using the Criteria API
  • caching settings
  • initial error handling, etc.

Therefore, the common practice is to create special classes for working with the database. Such classes are called DAO, Data Access Object. Their task is to hide all the complexities of working with the database and provide a beautiful and convenient interface to the outside.

Example:

public class EmployeeDAO {

   public List<Employee> getEmployeeList(int from, int count) {
   	String hqlQuery = “from Employee;
   	Query<Employee> query = session.createQuery(hqlQuery, Employee.class);
   	query.setFirstResult(from);
   	query.setMaxResults(count);
   	return query.getResultList();
  }

	public int getEmployeeCount() {
	     String hqlQuery = “select count(*) from Employee;
     	Query<Integer> query = session.createQuery(hqlQuery, Integer.class);
     	return query.getSingleResult();
   }

	public Employee getEmployeeByUniqName(String name) {
	     String hqlQuery = “from Employee where name = :name”;
     	Query<Integer> query = session.createQuery(hqlQuery, Employee.class);
     	query.setParameterr(“name”, name);
     	return query.getSingleResult();
   }
}

We have a class EmployeeDAO , with the help of which we get objects of type Employee from the database. The class itself, although stuffed with annotations, does not contain methods for saving itself to the database.

Benefits of DAO

There are many advantages to this approach:

First, we have completely hidden the work with the database in the DAO class. If you decide in the future to rewrite all queries from HQL to Criteria API or Native Query, this will not affect the code outside of this class in any way.

Secondly, you can complicate the behavior of these methods. You can add caching, events, parameter validation. This will all be hidden from the outside code.

Thirdly, if you need a method that doesn't exist yet, you just add it here. For example, I need a method that will return all user tasks that have already been expired. Then I'll just do this:

public class EmployeeDAO {

   public List<Task> getExpiredTasks(int userId, int from, int count) {
   	String hqlQuery = “from Task where task.user.id = :id and deadline < curdate();
   	Query<Task> query = session.createQuery(hqlQuery, Task.class);
   	query.setFirstResult(from);
   	query.setMaxResults(count);
   	return query.getResultList();
  }

   public int getExpiredTasksCount(int userId) {
   	String hqlQuery = “select count(*) from Task where task.user.id = :id and deadline < curdate();
   	Query<Integer> query = session.createQuery(hqlQuery, Integer.class);
   	return query.getSingleResult();
  }
}

I added two methods to the class:

  • getExpiredTasksCount() - returns the number of expired tasks for the user
  • getExpiredTasks() - returns a list of expired tasks for the user

I need methods - I added them. And I can use it right away. I'll optimize them later.

Moreover, these methods can be covered with unit tests before rewriting and optimizations, so we will know that the work with the database has remained the same as it was.

Standard Approach

Very often, DAO classes have methods that are the same. For example, these:

T getById (final long id) Get an object by its id
List<T> getItems (int from, int count) Get a list of objects within a given range
List<T> getAll () Get all objects of a given type
int getCount () Find out the number of objects
T save (final T entity) Save object to database
T update (final T entity) Update object in database
void delete (final T entity) Delete an object from the database
void deleteById (final long entityId) Delete object from database by id

These methods are found in almost every DAO class in the world. Now, if there is some kind of DAO class, then with 90% probability it will have such methods.

Yes, there may be others, but there will be those too. Because it's very convenient. And it allows you not to interact with the base or Hibernate directly.

And if there are identical methods, then what do they need? That's right, put it in the base class.

It looks something like this:

public abstract class AbstractHibernateDao<T > {
    private final Class<T> clazz;
    private SessionFactory sessionFactory;

    public AbstractHibernateDao(final Class<T> clazzToSet)   {
    	this.clazz = clazzToSet;
    }

    public T getById(final long id) {
    	return (T) getCurrentSession().get(clazz, id);
    }

    public List<T> getItems(int from, int count) {
    	Query query = getCurrentSession().createQuery(clazz , "from " + clazz.getName())
    	query.setFirstResult(offset);
    	query.setMaxResults(count);
  	  return query.singleResult();
    }

    public List<T> findAll() {
    	return getCurrentSession().createQuery(clazz, "from " + clazz.getName()).list();
    }

    public T create(final T entity) {
    	getCurrentSession().saveOrUpdate(entity);
    	return entity;
    }

    public T update(final T entity) {
    	return (T) getCurrentSession().merge(entity);
    }

    public void delete(final T entity) {
    	getCurrentSession().delete(entity);
    }

    public void deleteById(final long entityId) {
    	final T entity = getById(entityId);
    	delete(entity);
    }

    protected Session getCurrentSession() {
    	return sessionFactory.getCurrentSession();
    }
}

And then our EmployeeDAO will look like this:

public class EmployeeDAO extends AbstractHibernateDAO<Employee> {

   public EmployeeDAO (){
  	super(Employee.class );
   }
}

And TaskDAO is like this:

public class TaskDAO extends AbstractHibernateDAO<Task> {

   public TaskDAO (){
  	super(Task.class );
   }
}

And both of these classes will have all the methods we declared on AbstractHibernateDAO . Unification is very convenient and practical.