Схема DAO и принцип открытого закрывания

Я видел и работал с большим количеством более раннего кода DAO на основе JDBC, который обычно начинается с методов CRUD. Мой вопрос относится конкретно к методам поиска или "искателям". Обычно я нахожу, что DAO начинаются с двух методов:

  • найти и вернуть ВСЕ
  • получить конкретный экземпляр на основе уникального идентификатора

Чаще всего этих двух искателей недостаточно. Обычно я вижу, что класс DAO неоднократно модифицировался, чтобы добавить методы поиска, такие как:

  • найти и вернуть ВСЕ, где {условие}

Что происходит, так это то, что добавляются новые методы при необходимости поддержки новых {условий} или существующих методов для добавления новых параметров в качестве флагов для изменения SQL-запроса внутри метода для поддержки дополнительных условий.

Это уродливый подход и нарушает принцип открытого закрытия. Я всегда видел, как классы DAO постоянно изменяются, когда требуется какое-то новое условие поиска. Исследование этого вопроса часто указывает мне на шаблон хранилища и инкапсулирует условия для поиска как Технические характеристики или объекты запроса, а затем передает их методу поиска. Но это кажется возможным только в том случае, если у вас есть сборка в памяти всего набора данных или если вы используете какой-то ORM (я работаю со старым кодом JDBC)

Я рассмотрел решение, которое ленивы загружает весь набор данных, который DAO управляет как коллекция в памяти, а затем использует шаблон спецификации в качестве запросов для извлечения. Затем я реализую своего рода наблюдателя в коллекции, который просто обновляет базу данных при создании, обновлении или удалении методов. Но, очевидно, производительность и масштабируемость значительно страдают.

Любые мысли об этом?


Спасибо за ответы. У меня есть одна мысль - каково ваше мнение об использовании шаблона Command/Policy для инкапсуляции запросов доступа к данным? Каждая отдельная команда Concrete может представлять определенный вид доступа и может быть передана Invoker. Я бы закончил с многочисленными классами Concrete Command, но каждый из них будет ориентирован только на один вид доступа и должен быть очень надежным и изолированным.

    public abstract class Command<R>{
       public <R> execute();
       public void setArguments(CommandArguments args){
          //store arguments  
       }
    }

    //map based structure for storing and returning arguments
    public class CommandArguments{
         public String getAsString(String key);
         public String getAsInt(String key);
         //... others
    }

    //In some business class...
    Command command = CommandFactory.create("SearchByName");
    CommandArguments args = new CommandArguments();
    args.setValue("name", name);
    // others
    command.setArguments(args);
    List<Customer> list  = command.execute();

Ответ 1

Мы использовали iBatis для нашего ORM слоя данных и смогли реализовать то, что вы предлагаете в одном запросе, передав объект параметра с различными полями, которые вы, возможно, захотите использовать в качестве параметров.

Затем в вашем предложении WHERE вы можете указать каждое поле как условие условия, но только если оно заселено в объекте параметра. Если только одно поле в параметре obj не равно null, то оно будет единственным, которое будет использоваться для фильтрации результатов.

Таким образом, если вам нужно добавить поля к вашим параметрам, вы просто измените SQL и paramObj. Затем у вас есть 2 метода, которые возвращают ВСЕ или подмножество на основе комбо переданных параметров, или, по крайней мере, этот подход уменьшит количество требуемых запросов.

например. что-то вроде...

SELECT * FROM MY_TABLE
WHERE FIELD_ZERO = paramObj.field0
<isNotNull property="paramObj.field1">AND FIELD_ONE = paramObj.field1</isNotNull>
<isNotNull property="paramObj.field2">AND FIELD_TWO = paramObj.field2</isNotNull>
<isNotNull property="paramObj.field3">AND FIELD_THREE = paramObj.field3</isNotNull>

Ответ 2

Вместо того, чтобы создавать конкретный метод поиска для каждого возможного условия по мере их появления, почему бы не создать общий API-интерфейс поиска? Это может иметь форму DAO с внутренним Enum для представления полей и метод, который принимает список экземпляров внутреннего класса DAO с полями, представляющими, какое поле DAO фильтрует, какой фильтр применять к нему, и какое условие (И, ИЛИ и т.д.).

Это небольшая работа по настройке, и для небольших проектов может быть слишком много, но это, безусловно, возможно. Структуры ORM обычно имеют нечто похожее, уже встроенное, поэтому вы можете подумать о принятии одного из них (или, по крайней мере, глядя на то, как они его реализовали при разработке собственного решения для переоснащения в ваше устаревшее приложение).