Паттерн dao в java

Паттерн dao в java

При работе с базой данных через JDBC или даже через Hibernate код часто получается более громоздким, чем это хотелось бы. Запрос к базе часто содержит:

  • валидацию данных
  • установку параметров запроса
  • выбор HQL-запроса в зависимости от параметров запроса
  • конструирование запроса с помощью Criteria API
  • настройку кэширования
  • первичную обработку ошибок и т. п.

Поэтому общепринятая практика — создавать специальные классы для работы с базой данных. Такие классы получили название DAO, Data Access Object. Их задача — скрывать все сложности работы с базой и предоставлять наружу красивый и удобный интерфейс.

 public class EmployeeDAO < public ListgetEmployeeList(int from, int count) < String hqlQuery = “from Employee”; Queryquery = session.createQuery(hqlQuery, Employee.class); query.setFirstResult(from); query.setMaxResults(count); return query.getResultList(); > public int getEmployeeCount() < String hqlQuery = “select count(*) from Employee”; Queryquery = session.createQuery(hqlQuery, Integer.class); return query.getSingleResult(); > public Employee getEmployeeByUniqName(String name) < String hqlQuery = “from Employee where name = :name”; Queryquery = 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 ListgetExpiredTasks(int userId, int from, int count) < String hqlQuery = “from Task where task.user.id = :id and deadline < curdate()”; Queryquery = 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()”; Queryquery = session.createQuery(hqlQuery, Integer.class); return query.getSingleResult(); > > 

Я добавил в класс два метода:

  • getExpiredTasksCount() — возвращает количество проэкспареных задач у пользователя
  • getExpiredTasks() — возвращает список проэкспареных задач у пользователя

Мне нужны методы — я их добавил. И сразу могу использовать. А оптимизировать их буду потом.

Более того, эти методы можно покрыть Unit-тестами перед переписыванием и оптимизациями, таким образом мы будем знать, что работа с базой данных осталась такой, как и была.

Стандартный подход

Очень часто у DAO-классов есть методы, которые совпадают. Например, такие:

T getById (final long id) Получить объект по его id
List getItems (int from, int count) Получить список объектов по заданному диапазону
List 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  < private final Classclazz; private SessionFactory sessionFactory; public AbstractHibernateDao(final Class clazzToSet) < this.clazz = clazzToSet; >public T getById(final long id) < return (T) getCurrentSession().get(clazz, id); >public List getItems(int from, int count) < Query query = getCurrentSession().createQuery(clazz , "from " + clazz.getName()) query.setFirstResult(offset); query.setMaxResults(count); return query.singleResult(); >public List 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  < public EmployeeDAO ()< super(Employee.class ); >> 
 public class TaskDAO extends AbstractHibernateDAO  < public TaskDAO ()< super(Task.class ); >> 

И у обоих этих классов будут все методы, которые мы объявили у AbstractHibernateDAO . Унификация — это очень удобно и практично.

Источник

Data Access Object

Code that depends on specific features of data resources ties together business logic with data access logic. This makes it difficult to replace or modify an application’s data resources.

The Data Access Object (or DAO) pattern:

  • separates a data resource’s client interface from its data access mechanisms
  • adapts a specific data resource’s access API to a generic client interface

The DAO pattern allows data access mechanisms to change independently of the code that uses the data.

Detailed Description

See the Core J2EE TM Patterns

Detailed Example

The Java Pet Store sample application uses the DAO pattern both for database vendor-neutral data access, and to represent XML data sources as objects.

    Accessing a database with a DAO. A Data Access Object class can provide access to a particular data resource without coupling the resource’s API to the business logic. For example, sample application classes access catalog categories, products, and items using DAO interface CatalogDAO . Reimplementing CatalogDAO for a different data access mechanism (to use a Connector, for example), would have little or no impact on any classes that use CatalogDAO , because only the implementation would change. Each potential alternate implementation of CatalogDAO would access data for the items in the catalog in its own way, while presenting the same API to the class that uses it. The following code excerpts illustrate how the sample application uses the DAO pattern to separate business logic from data resource access mechanisms:

      Interface CatalogDAO defines the DAO API. Notice that the methods in the interface below make no reference to a specific data access mechanism. For example, none of the methods specify an SQL query, and they throw only exceptions of type CatalogDAOSysException . Avoiding mechanism-specific information in the DAO interface, including exceptions thrown, is essential for hiding implementation details.
     public interface CatalogDAO
     public class CloudscapeCatalogDAO implements CatalogDAO < . public static String GET_CATEGORY_STATEMENT = "select name, descn " + " from (category a join category_details b on a.catid=b.catid) " + " where locale = ? and a.catid = ?"; . public Category getCategory(String categoryID, Locale l) throws CatalogDAOSysException < Connection c = null; PreparedStatement ps = null; ResultSet rs = null; Category ret = null; try < c = getDataSource().getConnection(); ps = c.prepareStatement(GET_CATEGORY_STATEMENT,ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY); ps.setString(1, l.toString()); ps.setString(2, categoryID); rs = ps.executeQuery(); if (rs.first()) < ret = new Category(categoryID, rs.getString(1), rs.getString(2)); >rs.close(); ps.close(); c.close(); return ret; > catch (SQLException se) < throw new CatalogDAOSysException("SQLException: " + se.getMessage()); >> . > 
         select name, descn from (category a join category_details b on a.catid=b.catid) where locale = ? and a.catid = ?  .   select name, descn from category a, category_details b where a.catid = b.catid and locale = ? and a.catid = ?  .  

    Method GenericCatalogDAO.getCategory chooses the SQL corresponding to the configured database type, and uses it to fetch a category from the database via JDBC. The following code excerpt shows shows the implementation of the method.

     public Category getCategory(String categoryID, Locale locale) throws CatalogDAOSysException < Connection connection = null; ResultSet resultSet = null; PreparedStatement statement = null; try < connection = getDataSource().getConnection(); String[] parameterValues = new String[] < locale.toString(), categoryID >; statement = buildSQLStatement(connection, sqlStatements, XML_GET_CATEGORY, parameterValues); resultSet = statement.executeQuery(); if (resultSet.first()) < return new Category(categoryID, resultSet.getString(1), resultSet.getString(2)); >return null; > catch (SQLException exception) < throw new CatalogDAOSysException("SQLException: " + exception.getMessage()); >finally < closeAll(connection, statement, resultSet); >> 
     public static Screens loadScreenDefinitions(URL location) < Element root = loadDocument(location); if (root != null) return getScreens(root); else return null; >. public static Screens getScreens(Element root) < // get the template String defaultTemplate = getTagValue(root, DEFAULT_TEMPLATE); if (defaultTemplate == null) < System.err.println("*** ScreenDefinitionDAO error: " + " Default Template not Defined."); return null; >Screens screens = new Screens(defaultTemplate); getTemplates(root, screens); // get screens NodeList list = root.getElementsByTagName(SCREEN); for (int loop = 0; loop < list.getLength(); loop++) < Node node = list.item(loop); if ((node != null) && node instanceof Element) < String templateName = ((Element)node).getAttribute(TEMPLATE); String screenName = ((Element)node).getAttribute(NAME); HashMap parameters = getParameters(node); Screen screen = new Screen(screenName, templateName, parameters); if (!screens.containsScreen(screenName)) < screens.addScreen(screenName, screen); >else < System.err.println("*** Non Fatal errror: Screen " + screenName + " defined more than once in screen definitions file"); >> > return screens; > . 

    Источник

Оцените статью