#java #ооп #шаблоны_проектирования #dao
Всякий раз сталкиваясь с написанием очередного DAO, я споткаюсь об одну и ту же проблему - проектирование максимально абстрактного интерфейса DAO, удовлетворяющего нуждам Service Layer. Примерно так выглядит обычный DAO/Repository во всех Hello World. interface DAO { Entity get(Long id); ListgetAll(); void save(Entity e); void update(Entity e); void delete(Long id); } К сожалению, таких идеальных маленьких сущностей немного, и во всех приложениях, что я писал, требовалось прикрутить более-менее сложный поиск. Со всеми этими JOIN, GROUP BY, HAVING и прочим SQL, ну вы и так знаете. И вот в этом конкретном месте всегда оказывается, что выразить с помощью ООП критерии поиска, которые так просто и очевидно ложатся на SQL, та еще задачка. Обычно в таких случаях DAO начинает обрастать кучей методов, которые напрямую удовлетворяют нуждам сервиса. Часто до такой степени, что совпадают сигнатуры методов, и единственное что делает сервис, это оборачивает метод DAO в транзакцию. interface DAO { // немного утрируем, но для примера сойдет List findByUsernameAndStatusAndDate(String userName, Status status, Date lastLogged); Long countByUsernameAndStatusAndDate(String userName, Status status, Date lastLogged); //.. еще 100500 таких же методов } Как-то раз меня это порядком достало, и я написал примерно следующее: interface DAO { List find(String queryName, Param[]... params); Long count(String queryName, Param[]... params); } class Param { String name; V value; } Второй способ предлагает более гибкий интерфейс, но при этом мы имеем leaky abstraction, поскольку детали реализация DAO "протекли" в сервис. За последнее время я перелопатил просто кучу материала, пытаясь найти какой-то универсальный принцип написания "search API", который можно применить к DAO. Нашел несколько способов: Query By Example - в качестве параметров поиска передается объект того же класса, который мы ищем. Подходит только для простых случаев. JPA Criteria API - можно передать условия поиска в виде списка предикатов. Очень выразительный API, который, к сожалению, реализован только для JPA. Писать аналог для JDBC долго и муторно. Так назывемый Specification - нигде толком не описан, напоминает вырожденный Criteria API. Довольно многословный, и с треском ломается при попытке описать что-то более-менее сложное. Вопросы: Явлется ли протекающий в сервис DAO таким уж "code smell"? Стоит ли вообще стремиться реализовать поиск с помощью abstract/generic DAO или лучше и дальше пачками клепать специфичные методы под каждый новый use case и не выпендриваться?
Ответы
Ответ 1
Я обычно создаю абстрактный обобщенный DAO класс, в котором описаны общие CRUD операции и что-то чуть более, как то поиск по ids, который можно незатрано и понятно описать на генериках. Остальное специфичное уходит уже в конкретную реализацию db/jpa сервиса. По ходу проекта с пониманием требований обычно выявляются общие места, которые можно/нужно вынести в родительский класс. Я думаю, автор гонится за прекрасным. DAO не таблетка от всех бед.Ответ 2
В одной из задач я использовал объект поиска, это отдельный класс, который описывает все возможное поля поиска. Это была учебная задача с книгами и надо было создать поиск по всем полям (автор, название, издательство, дата выпуска точная или промежуток). На ваш суд предложу такое решение. interface Template{ // метод вернет готовую строку с параметрами // param1 = param and param2 = param2... String getfindString(); } И у вас есть сущность книги: class Book{ String author; String title; Date date; } Остается создать сущность для поиска: class BookFindTemplate implements Template{ String author; String title; Date afterDate; Date beforeDate; String getfindString(){ String Builder query = new StringBuilder(); query.append(формируем условие запроса, которое идет после where); //в моем исполнении логика была очень страшная. return query.toString(); } } Тогда в ДАО у вас остаются методы поиска, в которые вы передаете образец поиска, он уже сообщит что искать и как искать, вам остается указать в какой таблице это искать. Вопрос где держать эти образцы, в дао или в модели. Т.к. там формируется часть sql запроса, думаю стоит поближе к ДАО.Ответ 3
Ну если быть откровенным, то DAO не очень подходит для таких задач. Данный паттерн предназначен только для CRUD методов, в то время как Repository, для этого и предназначен, он имеет специфические запросы которые для DAO не подойдут. Так что с понятием Repository и для чего он нужен вроде как разобрали. По абстракции. Я же все таки использовал бы максимально простой подход и более гибкий подход. Если сигнатуры совпадают, ничего страшного. Если приложение будет большим, то ты потом будешь скучать по таким "совпадениям". Следует помнить что сервис для логики, а репозиторий для работы с БД, и если ты через бд получаешь все что нужно, то это хорошо.
Комментариев нет:
Отправить комментарий