Является ли функционально-реляционное сопоставление проще, чем объект-реляционное? - PullRequest
16 голосов
/ 20 октября 2008

Объектно-реляционное отображение было хорошо обсуждено, в том числе и здесь. У меня есть опыт с несколькими подходами и подводными камнями и компромиссами. Истинное разрешение, похоже, требует изменений в самих ОО или реляционных моделях.

При использовании функционального языка возникает ли та же проблема? Мне кажется, что эти две парадигмы должны лучше сочетаться, чем ОО и СУРБД. Идея мышления в множествах в СУБД, кажется, сочетается с автоматическим параллелизмом, который обещают функциональные подходы.

Есть ли у кого-нибудь интересные мнения или идеи? Каково состояние дел в отрасли?

Ответы [ 7 ]

10 голосов
/ 09 октября 2018

Какова цель ORM?

Основной целью использования ORM является мост между сетевой моделью (ориентация объекта, графики и т. Д.) И реляционной моделью. И главное отличие двух моделей на удивление простое. Вопрос в том, указывают ли родители на детей (сетевая модель) или дети указывают на родителей (реляционная модель).

Учитывая эту простоту, я считаю, нет такой вещи, как "несоответствие импеданса" между двумя моделями. Проблемы, с которыми обычно сталкиваются люди, связаны исключительно с реализацией и должны решаться, если бы существовали лучшие протоколы передачи данных между клиентами и серверами.

Как SQL может решить проблемы, возникающие у нас с ORM?

В частности, третий манифест пытается устранить недостатки языка SQL и реляционной алгебры, допуская вложенные коллекции, которые были реализованы в различных базах данных, включая:

  • Oracle (возможно, самая сложная реализация)
  • PostgreSQL (в некоторой степени)
  • Informix
  • SQL Server, MySQL и т. Д. (Через «эмуляцию» через XML или JSON)

По моему мнению, если бы во всех базах данных был реализован стандартный оператор SQL MULTISET() (например, в Oracle), люди больше не использовали бы ORM для отображения (возможно, все еще для сохранения графов объектов), потому что они могли материализовать вложенные коллекции непосредственно из базы данных, например этот запрос:

SELECT actor_id, first_name, last_name,
  MULTISET (
    SELECT film_id, title
    FROM film AS f
    JOIN film_actor AS fa USING (film_id)
    WHERE fa.actor_id = a.actor_id
  ) AS films
FROM actor AS a

Даст все актеры и их фильмы как вложенную коллекцию, а не денормализованный результат объединения (где актеры повторяются для каждого фильма).

Функциональная парадигма на стороне клиента

Вопрос о том, подходит ли функциональный язык программирования на стороне клиента для взаимодействия с базой данных, действительно ортогональн. ORM помогают с сохранением графов объектов, поэтому, если ваша клиентская модель представляет собой граф, и вы хотите, чтобы он был графом, вам потребуется ORM, независимо от того, манипулируете ли вы этим графом с помощью функционального языка программирования.

Однако, поскольку в функциональных языках программирования ориентация объекта менее идиоматична, вы с меньшей вероятностью включите каждый элемент данных в объект. Для тех, кто пишет SQL, проецирование произвольных кортежей очень естественно. SQL охватывает структурную типизацию. Каждый запрос SQL определяет свой собственный тип строки без необходимости предварительно назначать ему имя. Это очень хорошо согласуется с функциональными программистами, особенно когда сложный вывод типов, в случае которого вы никогда не будете думать о сопоставлении своего результата SQL с каким-либо ранее определенным объектом / классом.

Пример в Java, использующий jOOQ из этого сообщения в блоге может быть:

// Higher order, SQL query producing function:
public static ResultQuery<Record2<String, String>> actors(Function<Actor, Condition> p) {
    return ctx.select(ACTOR.FIRST_NAME, ACTOR.LAST_NAME)
              .from(ACTOR)
              .where(p.apply(ACTOR)));
}

Этот подход приводит к гораздо лучшей композиционности операторов SQL, чем если бы язык SQL был абстрагирован каким-либо ORM или если использовалась естественная "строковая" природа SQL. Вышеупомянутая функция теперь может быть использована, например, как это:

// Get only actors whose first name starts with "A"
for (Record rec : actors(a -> a.FIRST_NAME.like("A%")))
    System.out.println(rec);

FRM абстракция над SQL

Некоторые FRM пытаются абстрагироваться от языка SQL, обычно по следующим причинам:

  • Они утверждают, что SQL недостаточно композируем (jOOQ это опровергает, просто очень трудно понять, что правильно).
  • Они утверждают, что пользователи API более привыкли к "нативным" API коллекции, например, JOIN переводится в flatMap() и WHERE переводится в filter() и т. Д.

Чтобы ответить на ваш вопрос

FRM не "проще", чем ORM, он решает другую проблему. На самом деле FRM вообще не решает никаких проблем, потому что SQL, сам по себе являющийся декларативным языком программирования (который ничем не отличается от функционального программирования), очень хорошо подходит для других функциональных языков программирования клиента. Таким образом, FRM просто устраняет разрыв между SQL, внешним DSL и языком вашего клиента.

(я работаю в компании за jOOQ , поэтому этот ответ предвзят)

8 голосов
/ 20 октября 2008

Сложными проблемами расширения реляционной базы данных являются расширенные транзакции, несоответствия типов данных, автоматический перевод запросов и такие вещи, как N + 1 Select , которые являются фундаментальными проблемами ухода из реляционной системы и - в моем мнение - не меняйте, меняя принимающую парадигму программирования.

4 голосов
/ 01 июля 2016

Это зависит от ваших потребностей

  1. Если вы хотите сосредоточиться на структурах данных, используйте ORM, такой как JPA / Hibernate
  2. Если вы хотите пролить свет на процедуры, взгляните на библиотеки FRM: QueryDSL или Jooq
  3. Если вам нужно настроить запросы SQL на конкретные базы данных, используйте JDBC и собственные запросы SQL

Сила различных технологий «реляционного сопоставления» - это мобильность: вы гарантируете, что ваше приложение будет работать на большинстве баз данных ACID. В противном случае вы будете справляться с различиями между различными диалектами SQL, когда будете писать запросы SQL вручную.

Конечно, вы можете ограничить себя стандартом SQL92 (а затем заняться функциональным программированием) или использовать концепции концептуального программирования с помощью фреймворков ORM

Сильные стороны ORM построены на объекте сеанса, который может выступать в качестве узкого места:

  1. он управляет жизненным циклом объектов, пока выполняется базовая транзакция базы данных.
  2. он поддерживает взаимно-однозначное сопоставление между вашими java-объектами и строками базы данных (и использует внутренний кэш, чтобы избежать дублирования объектов).
  3. автоматически обнаруживает обновления ассоциации и потерянные объекты для удаления
  4. обрабатывает проблемы с оптимистической или пессимистической блокировкой.

Тем не менее, его сильные и слабые стороны:

  1. Сессия должна иметь возможность сравнивать объекты, поэтому вам нужно реализовать методы equals / hashCode. Но равенство объектов должно основываться на «бизнес-ключах», а не на идентификаторе базы данных (новые временные объекты не имеют идентификатора базы данных!). Тем не менее, некоторые усовершенствованные концепции не имеют делового равенства (например, операция). Обычный обходной путь основан на идентификаторах GUID, которые, как правило, расстраивают администраторов баз данных.

  2. Сеанс должен шпионить за изменениями отношений, но его правила отображения толкают использование коллекций, непригодных для бизнес-алгоритмов. Когда-нибудь вы захотите использовать HashMap, но ORM потребует, чтобы ключом был другой «Rich Domain Object» вместо другого более легкого ... Затем вы должны реализовать равенство объектов для объекта расширенного домена, выступающего в качестве ключа ... Но вы не можете, потому что этот объект не имеет аналогов в мире бизнеса. Таким образом, вы возвращаетесь к простому списку, по которому вам нужно перебирать (и возникают проблемы с производительностью).

  3. ORM API иногда не подходит для реального использования. Например, реальные веб-приложения пытаются обеспечить изоляцию сеанса, добавляя некоторые предложения "WHERE" при получении данных ... Тогда «Session.get (id)» не достаточно, и вам нужно обратиться к более сложному DSL (HSQL, Criteria API) или вернуться к собственному SQL

  4. Объекты базы данных конфликтуют с другими объектами, выделенными для других платформ (например, фреймворки OXM = сопоставление объектов / XML). Например, если ваши службы REST используют библиотеку Джексона для сериализации бизнес-объекта. Но этот Джексон точно соответствует Hibernate. Тогда либо вы объединяете оба, и появляется сильная связь между вашим API и вашей базой данных Или вы должны осуществить перевод, и весь код, который вы сохранили из ORM, там потерян ...

С другой стороны, FRM - это компромисс между «объектно-реляционным отображением» (ORM) и собственными SQL-запросами (с JDBC)

Лучший способ объяснить различия между FRM и ORM заключается в применении подхода DDD.

  • Object Relational Mapping дает возможность использовать «Rich Domain Object», которые являются классами Java, чьи состояния могут изменяться во время транзакции базы данных
  • Функциональное реляционное сопоставление опирается на «плохие доменные объекты», которые являются неизменяемыми (настолько, что вам приходится клонировать новый каждый раз, когда вы хотите изменить его содержимое)

Он снимает ограничения, накладываемые на сеанс ORM, и полагается большую часть времени на DSL поверх SQL (поэтому переносимость не имеет значения)Но с другой стороны, вам нужно изучить детали транзакции, проблемы параллелизма

List<Person> persons = queryFactory.selectFrom(person)
  .where(
    person.firstName.eq("John"),
    person.lastName.eq("Doe"))
  .fetch();
3 голосов
/ 20 октября 2008

Я не сделал функционально-реляционное отображение, само по себе , но я использовал методы функционального программирования для ускорения доступа к СУБД.

Довольно часто начинать с набора данных, выполнять на нем сложные вычисления и сохранять результаты, где, например, результаты представляют собой подмножество оригинала с дополнительными значениями. Императивный подход требует, чтобы вы сохранили исходный набор данных с дополнительными столбцами NULL, выполнили вычисления, а затем обновили записи с вычисленными значениями.

Кажется разумным. Но проблема в том, что это может быть очень медленным. Если ваше вычисление требует другого оператора SQL помимо самого запроса на обновление, или даже должно быть выполнено в коде приложения, вы буквально должны (повторно) искать записи, которые вы изменяете после вычисления, чтобы сохранить ваши результаты в правильных строках .

Вы можете обойти это, просто создав новую таблицу для результатов. Таким образом, вы всегда можете вставить вместо обновления. В итоге получается еще одна таблица, дублирующая ключи, но вам больше не нужно тратить пространство на столбцы, хранящие NULL & ndash; Вы храните только то, что имеете. Затем вы присоединяете свои результаты к окончательному выбору.

Я (ab) использовал СУБД таким образом и в итоге написал операторы SQL, которые выглядели в основном так ...

create table temp_foo_1 as select ...;
create table temp_foo_2 as select ...;
...
create table foo_results as
  select * from temp_foo_n inner join temp_foo_1 ... inner join temp_foo_2 ...;

То, что это делает по сути, создает группу неизменных привязок. Хорошая вещь, однако, в том, что вы можете работать с целыми сетами одновременно. В некотором роде напоминает вам языки, которые позволяют работать с матрицами, например Matlab.

Полагаю, это намного упростит параллелизм.

Дополнительным преимуществом является то, что типы столбцов для таблиц, созданных таким образом, указывать не нужно, поскольку они выводятся из столбцов, из которых они выбраны.

3 голосов
/ 20 октября 2008

Я думаю, что, как сказал Сэм, для обновления БД необходимо столкнуться с теми же проблемами параллелизма, что и с миром OO. Функциональная природа программы может быть даже немного более проблематичной, чем природа объекта из-за состояния данных, транзакций и т. Д. СУБД.

Но для чтения функциональный язык может быть более естественным с некоторыми проблемными областями (как, кажется, независимо от БД)

Функциональное отображение <-> RDBMS не должно иметь больших отличий от отображений OO <-> RDMBS. Но я думаю, что это во многом зависит от того, какие типы данных вы хотите использовать, если вы хотите разработать программу с новой схемой БД или сделать что-то против устаревшей схемы БД и т. Д.

Ленивые выборки и т. Д. Для ассоциаций, например, возможно, будут реализованы довольно хорошо с некоторыми концепциями, связанными с ленивой оценкой. (Несмотря на то, что с OO их тоже можно сделать довольно красиво)

Редактировать: При поиске я нашел HaskellDB (библиотека SQL для Haskell) - что может стоить попробовать?

3 голосов
/ 20 октября 2008

Я предполагаю, что функционал для реляционного отображения должен быть проще в создании и использовании, чем OO для RDBMS. Пока вы только запрашиваете базу данных, то есть. Я пока не вижу (как), как вы могли бы делать обновления базы данных без побочных эффектов хорошим способом.

Основная проблема, которую я вижу, это производительность. Сегодняшние СУБД не предназначены для использования с функциональными запросами и, вероятно, будут вести себя плохо во многих случаях.

0 голосов
/ 12 мая 2019

Базы данных и функциональное программирование могут быть объединены.

например:

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

               Clojure -> DBMS, Super Foxpro
                   STM -> Transaction,MVCC
Persistent Collections -> db, table, col
              hash-map -> indexed data
                 Watch -> trigger, log
                  Spec -> constraint
              Core API -> SQL, Built-in function
              function -> Stored Procedure
             Meta Data -> System Table

Примечание. В последней спецификации spec2 спецификация больше похожа на RMDB. см .: spec-alpha2 wiki: Схема и выбор

Я защищаю: построение реляционной модели данных поверх хэш-карты для достижения сочетания преимуществ NoSQL и RMDB. На самом деле это обратная реализация posgtresql.

Duck Typing: Если это похоже на утку и крякает как утка, значит, это утка.

Если модель данных clojure, такая как RMDB, средства clojure, такие как RMDB, и манипулирование данными clojure, такие как RMDB, clojure должен быть RMDB.

Clojure - это функциональный язык программирования, основанный на теории реляционных баз данных

Все RMDB

Реализация реляционной модели данных и программирования на основе хэш-карты (NoSQL)

...