Знакомство с DAO
При работе с базой данных через JDBC или даже через Hibernate код часто получается более громоздким, чем это хотелось бы. Запрос к базе часто содержит:
- валидацию данных
- установку параметров запроса
- выбор HQL-запроса в зависимости от параметров запроса
- конструирование запроса с помощью Criteria API
- настройку кэширования
- первичную обработку ошибок и т. п.
Поэтому общепринятая практика — создавать специальные классы для работы с базой данных. Такие классы получили название DAO, Data Access Object. Их задача — скрывать все сложности работы с базой и предоставлять наружу красивый и удобный интерфейс.
Пример:
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.setParametуr(“name”, name);
return query.getSingleResult();
}
}
У нас есть класс EmployeeDAO, с помощью которого мы получаем из базы данных объекты типа Employee. Сам класс, хоть и напичкан аннотациями, не содержит методов по сохранению себя в базу.
Преимущества DAO
Преимуществ у такого подхода очень много:
Во-первых, мы полностью скрыли в DAO-классе работу с базой данных. Если ты в будущем решишь переписать все запросы с HQL на Criteria API или на Native Query, это никак не затронет код за границами этого класса.
Во-вторых, ты можешь усложнять поведение этих методов. Можно добавить кеширование, events, валидацию параметров. Это все будет скрыто от кода снаружи.
В-третьих, если тебе нужен метод, которого еще нет, ты просто добавляешь его сюда. Например, мне нужен метод, который вернет все задачи пользователя, которые уже проэкспарились. Тогда я просто сделают так:
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();
}
}
Я добавил в класс два метода:
- getExpiredTasksCount() — возвращает количество проэкспареных задач у пользователя
- getExpiredTasks() — возвращает список проэкспареных задач у пользователя
Мне нужны методы — я их добавил. И сразу могу использовать. А оптимизировать их буду потом.
Более того, эти методы можно покрыть Unit-тестами перед переписыванием и оптимизациями, таким образом мы будем знать, что работа с базой данных осталась такой, как и была.
Стандартный подход
Очень часто у DAO-классов есть методы, которые совпадают. Например, такие:
T getById(final long id) | Получить объект по его id |
List<T> getItems(int from, int count) | Получить список объектов по заданному диапазону |
List<T> getAll() | Получить все объекты данного типа |
int getCount() | Узнать количество объектов |
T save(final T entity) | Сохранить объект в базу |
T update(final T entity) | Обновить объект в базе |
void delete(final T entity) | Удалить объект из базы |
void deleteById(final long entityId) | Удалить объект из базы по id |
Эти методы встречаются практически у каждого DAO-класса в мире. Вот если есть какой-то DAO-класс, то с 90% вероятностью у него будут такие методы.
Да, могут быть и другие, но такие тоже будут. Потому что это очень удобно. И позволяет не взаимодействовать с базой или Hibernate напрямую.
А если есть одинаковые методы, то их нужно что? Правильно, вынести в базовый класс.
Выглядит он примерно так:
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();
}
}
И тогда наш EmployeeDAO будет выглядеть так:
public class EmployeeDAO extends AbstractHibernateDAO<Employee> {
public EmployeeDAO (){
super(Employee.class );
}
}
А TaskDAO — так:
public class TaskDAO extends AbstractHibernateDAO<Task> {
public TaskDAO (){
super(Task.class );
}
}
И у обоих этих классов будут все методы, которые мы объявили у AbstractHibernateDAO. Унификация — это очень удобно и практично.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ