1. Знайомство з DAO
Під час роботи з базою даних через JDBC або навіть через Hibernate код часто виходить більш громіздким, ніж хотілося б. Запит до бази часто містить:
- валідацію даних
- встановлення параметрів запиту
- вибір HQL-запиту в залежності від параметрів запиту
- конструювання запиту за допомогою Criteria API
- налаштування кешування
- первинну обробку помилок тощо.
Тому загальноприйнята практика — створювати спеціальні класи для роботи з базою даних. Такі класи отримали назву DAO, Data Access Object. Їхнє завдання — приховувати всі складнощі роботи з базою та надавати гарний та зручний інтерфейс.
Приклад:
public class EmployeeDAO {
public List<Employee> getEmployeeList(int from, int count) {
String hqlQuery = "від 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 = "з Employee where name = :name";
Query<Integer>query = session.createQuery(hqlQuery, Employee.class);
query.setParameter(“name”, name);
return query.getSingleResult();
}
}
У нас є клас EmployeeDAO, за допомогою якого ми отримуємо з бази даних об'єкти типу Employee. Сам клас, хоч і напханий анотаціями, не містить методів збереження себе в базу.
2. Переваги DAO
Переваг такого підходу дуже багато:
По-перше, ми повністю приховали у DAO-класі роботу з базою даних. Якщо ти в майбутньому вирішиш переписати всі запити з HQL на Criteria API або Native Query, це жодним чином не зачепить код за межами цього класу.
По-друге, ти можеш ускладнювати поведінку цих методів. Можна додати кешування, events, валідацію параметрів. Це все буде приховано від коду зовні.
По-третє, якщо тобі потрібний метод, якого ще немає, ти просто додаєш його сюди. Наприклад, мені потрібен метод, який поверне всі завдання користувача, які вже були проекспайрені. Тоді я просто зроблю так:
public class EmployeeDAO {
public List<Task> getExpiredTasks(int userId, int from, int count) {
String hqlQuery = “з 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-тестами перед переписуванням та оптимізаціями, таким чином ми знатимемо, що робота з базою даних залишилася такою, як і була.
3. Стандартний підхід
Дуже часто 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 & 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. Уніфікація — це дуже зручно та практично.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ