Introdução ao DAO
Ao trabalhar com um banco de dados por meio do JDBC ou mesmo do Hibernate, o código geralmente se torna mais complicado do que gostaríamos. Uma consulta de banco de dados geralmente contém:
- data de validade
- definir parâmetros de solicitação
- Seleção de consulta HQL dependendo dos parâmetros de consulta
- construindo uma consulta usando a Criteria API
- configurações de cache
- tratamento inicial de erros, etc.
Portanto, a prática comum é criar classes especiais para trabalhar com o banco de dados. Essas classes são chamadas de DAO, Data Access Object. A tarefa deles é ocultar todas as complexidades do trabalho com o banco de dados e fornecer uma interface bonita e conveniente para o exterior.
Exemplo:
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();
}
}
Temos uma classe EmployeeDAO , com a qual obtemos objetos do tipo Employee do banco de dados. A própria classe, embora repleta de anotações, não contém métodos para salvar a si mesma no banco de dados.
Benefícios do DAO
Há muitas vantagens nessa abordagem:
Primeiro, ocultamos completamente o trabalho com o banco de dados na classe DAO. Se você decidir no futuro reescrever todas as consultas de HQL para Criteria API ou Native Query, isso não afetará o código fora desta classe de forma alguma.
Em segundo lugar, você pode complicar o comportamento desses métodos. Você pode adicionar cache, eventos, validação de parâmetros. Tudo isso ficará oculto do código externo.
Em terceiro lugar, se você precisar de um método que ainda não existe, basta adicioná-lo aqui. Por exemplo, preciso de um método que retorne todas as tarefas do usuário que já expiraram. Então eu vou fazer isso:
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();
}
}
Eu adicionei dois métodos à classe:
- getExpiredTasksCount() - retorna o número de tarefas expiradas para o usuário
- getExpiredTasks() - retorna uma lista de tarefas expiradas para o usuário
Eu preciso de métodos - eu os adicionei. E eu posso usá-lo imediatamente. Vou otimizá-los mais tarde.
Além disso, esses métodos podem ser cobertos com testes de unidade antes de reescrever e otimizações, assim saberemos que o trabalho com o banco de dados permaneceu o mesmo.
Abordagem Padrão
Muitas vezes, as classes DAO têm métodos que são os mesmos. Por exemplo, estes:
T getById (ID longo final) | Obter um objeto por seu id |
List<T> getItems (int from, int count) | Obtenha uma lista de objetos dentro de um determinado intervalo |
Lista<T> getAll () | Obter todos os objetos de um determinado tipo |
int getCount () | Descubra o número de objetos |
T save (entidade T final) | Salvar objeto no banco de dados |
Atualização T (entidade T final) | Atualizar objeto no banco de dados |
void deletar (entidade T final) | Excluir um objeto do banco de dados |
void deleteById (entityId longo final) | Excluir objeto do banco de dados por id |
Esses métodos são encontrados em quase todas as classes DAO do mundo. Agora, se houver algum tipo de classe DAO, com 90% de probabilidade ela terá esses métodos.
Sim, pode haver outros, mas haverá aqueles também. Porque é muito conveniente. E permite que você não interaja diretamente com a base ou com o Hibernate.
E se houver métodos idênticos, o que eles precisam? Isso mesmo, coloque na classe base.
Parece algo assim:
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();
}
}
E então nosso EmployeeDAO ficará assim:
public class EmployeeDAO extends AbstractHibernateDAO<Employee> {
public EmployeeDAO (){
super(Employee.class );
}
}
E TaskDAO é assim:
public class TaskDAO extends AbstractHibernateDAO<Task> {
public TaskDAO (){
super(Task.class );
}
}
E ambas as classes terão todos os métodos que declaramos em AbstractHibernateDAO . A unificação é muito conveniente e prática.
GO TO FULL VERSION