2016-03-04 3 views
1

Я изучаю шаблон объекта доступа к данным, который обеспечивает доступ к источнику данных, например базе данных. This answer на другой вопрос дает следующий пример:Как сделать объекты доступа к данным неблокирующими?

interface EmployeeDAO { 
    List<Employee> findAll(); 
    List<Employee> findById(); 
    List<Employee> findByName(); 
    boolean insertEmployee(Employee employee); 
    boolean updateEmployee(Employee employee); 
    boolean deleteEmployee(Employee employee); 
} 

я вижу подобные примеры в других ответах и ​​статей по всему Интернету. Меня смущает тот факт, что чтение и запись в базу данных часто занимает некоторое время, и в этом случае эти примеры (особенно find...()) не были бы очень практичными, насколько я могу судить. То есть блокировка во время вызова find...(), вероятно, будет нежелательным.

Я думаю, что было бы разумно создать интерфейс Listener (EmployeeDAO.Listener) с помощью таких методов, как void EmployeeFound(Employee employee), но я удивлен, что я не видел этого раньше в примерах DAO. Интересно, я просто не понимаю объекты доступа к данным и/или если у меня отсутствует более очевидный подход.

ответ

2

Есть целый ряд различных вариантов/подходов, которые часто принимаются:

  1. Блокировка как API вы показали. Это очень простой API для использования, и параллелизм может быть достигнут путем вызова API из многопоточного приложения.

  2. Получение/регистрация обработчиков в асинхронных операциях. Это иногда предоставляется в сочетании с API блокировки (и, фактически, может быть реализовано с точки зрения API блокировки просто путем создания фонового потока, а затем последующего вызова обработчика в конце).

  3. Возвращения будущего или ListenableFuture объекта, который делает интерфейс более идиоматический Java-эск (путем возврата данных в позиции возврата типа), но представляя конечный результат, а не сразу же доступен результат. Будущее можно использовать для блокировки или неблокирования.

Лично моя рекомендация здесь будет:

interface EmployeeDatabase { 
    interface StringWhereClause { 
    ListQueryBuilder is(String value); 
    ListQueryBuilder contains(String value); 
    ListQueryBUilder matchesRegex(String regex); 
    } 

    interface IntWhereClause { 
    ListQueryBuilder is(int value); 
    ListQueryBuilder isInRange(int min, int max); 
    ListQueryBuilder isGreaterThan(int value); 
    ListQueryBUilder isLessThan(int value); 
    ListQueryBuilder isGreaterThanOrEqualTo(int value); 
    ListQueryBUilder isLessThanOrEqualTo(int value); 
    } 
    // ... matchers for other types of properties ... 

    interface ListQueryBuilder { 
     // Generic restrict methods 
     StringWhereClause whereStringProperty(String propertyName); 
     IntWhereClause whereIntProperty(String propertyName); 
     // ... 

     // Named restrict methods 
     StringWhereClause whereName(); 
     StringWhereClause whereJobTitle(); 
     IntWhereClause whereEmployeeNumber(); 
     // ... 

     ListQueryBuilder limit(long maximumSize); 
     ListQueryBuilder offset(long index); 

     ResultSet<Employee> fetch(); 
    } 

    ListQueryBuilder list(); 
    ListenableFuture<Employee> getById(Key key); 
    ListenableFuture<KeyOrError> add(Employee employee); 
    ListenableFuture<Status> update(Key key, Employee employee); 
    ListenableFuture<Status> delete(Key key); 
} 

С:

interface ResultSet<T> { 
    Iterable<T> asIterable(); 
    // ... other methods ... 
} 

interface KeyOrError { 
    boolean isError(); 
    boolean isKey(); 
    Key getKey(); 
    Throwable getError(); 
} 

interface Status { 
    boolean isOk(); 
    boolean isError(); 
    Throwable getError(); 
    void verifyOk(); 
} 

В принципе, идея заключается в том, что введение в базу данных возвращает ключевой объект (или ошибка, если неудачный). Этот ключ можно использовать для извлечения, удаления или обновления записи в базе данных.Эти операции (добавление, обновление, удаление и getById) имеют единственный результат, и в этом случае вместо T используется ListenableFuture<T>; этот будущий объект позволяет блокировать (путем вызова .get() на будущий объект) или для асинхронного извлечения объекта (путем регистрации обратного вызова, который будет вызываться, когда результат будет готов).

Для операций list-y существует множество способов, по которым список может быть отфильтрован, подселеклен, отсортирован и т. Д. Чтобы предотвратить комбинаторный взрыв различных различных перегрузок, мы используем builder pattern, чтобы эти ограничения могли применяется в нескольких комбинациях. Короче говоря, интерфейс построителя обеспечивает способ привязки к нулю или более параметров (сортировки, фильтры, ограничения и т. Д.) Для применения операции поиска до вызова fetch(), который вызывает выполнение запроса списка, и возвращает ResultSet. Эта операция возвращает ResultSet, а не ListenableFuture, так как результаты не все возвращаются сразу (например, они могут возвращаться из базы данных потоковым способом); ResultSet - это фактически интерфейс с аналогичным поведением с ListenableFuture, но для списков элементов (где элементы могут быть готовы в разное время). Для удобства важно иметь возможность легко перебирать содержимое ResultSet (например, путем предоставления адаптера Iterable ResultSet); однако, вероятно, вам также захочется добавить другие методы, которые позволят вам выполнять другие типы асинхронной обработки на ResultSet; например, вам может понадобиться ListenableFuture<T> reduce(T initialValue, ReduceFunction<T> reducer) для агрегирования элементов в наборе результатов и предоставления будущего объекта, представляющего возможное завершение этого.

+0

Спасибо! Не могли бы вы добавить несколько строк объяснения к вашему примеру? – Anthony

0

Методы, которые вы реализуете в интерфейсе выше, будут простыми sql-запросами.

  • Найти все будет «выбрать * из таблицы»
  • Поиск по номеру собирается быть «выберите * из таблицы, где ID =: идентификатор» (который является первичным ключом таблицы - и индексируется).

Я работаю над приложением, в котором мы выполняем миллионы вставных операторов в день, и мы не беспокоимся о блокировке. Если вы работаете в java и используете Spring Framework, есть библиотеки, которые позаботятся обо всем этом для вас. Проверьте EntityManager и TransactionManager в java.persistence.

Смежные вопросы