Объектно-ориентированные структуры в реляционных базах данных

Близкие,

В n-й раз подряд я снова сталкиваюсь с той же старой проблемой. Речь идет о том, «как безболезненно сопоставить структуры ООП с таблицами базы данных».

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

В настоящее время моя схема базы данных включает таблицы для:

Актеры:

  • рабочий
  • работодатель
  • контакт

Сущности:

  • коммуникация
  • Примечания
  • и т.п.

Таблицы ассоциаций между сущностями и актерами:

  • рабочий-общение-задница
  • общение с работодателем
  • рабочий-заметки-задница
  • и т.д., вы получите дрель.

Мне это кажется "запахом кода". Каждый раз, когда заказчик меняет свою роль (т. Е. Повышается с «контактного лица» до «работодателя»), необходимо запускать кучу сумасшедших сценариев. Уф ... С другой стороны, если бы я работал в мире, основанном исключительно на ООП, это было бы намного проще - иметь базовый класс для всех сущностей с общими свойствами и покончить с этим ...

В мире БД этот вариант кажется теоретически возможным, но звучит очень запутанно ... То есть, если я правильно это понимаю, у меня будет новая таблица base_actor, и у каждого другого актера будет base_actor_id, и тогда ассоциации будут между base_actor и сущности ... Но тогда как мне выполнять запросы обратной связи? Т.е. «покажите мне все связи с просто актерами типа worker»?

Любой совет? Есть какие-нибудь общие мысли по поводу «сопоставления структур ООП с реляционной БД»?

Ответов (11)

Решение

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

  1. Смоделируйте все отношения наследования как соединения между таблицами. Каждая таблица в вашей системе будет содержать атрибуты определенного класса.

  2. Используйте синтетический идентификатор объекта (oid) в качестве первичного ключа для всех объектов. Генератор последовательности или столбец автоинкремента необходим для генерации значений oid.

  3. Все унаследованные классы должны использовать тот же тип oid, что и их родительский. Определите oid как внешний ключ с каскадным удалением. Родительская таблица получает столбец autoincrement oid, а дочерние - простые столбцы oid.

  4. Запросы по выпускным классам производятся в соответствующей таблице. Вы можете либо присоединить все таблицы родительских классов к запросу, либо просто загрузить необходимые атрибуты. Если у вас глубокая иерархия наследования и у вас много классов, пакет ORM действительно может упростить ваш код. В моей системе было менее 50 классов с максимальной глубиной наследования 3.

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

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

Вот как выглядит «покажи мне все коммуникации только с актерами типа worker».

select * from comm c, worker w where c.actor=w.oid;

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

select * from comm c, worker w, missive m where c.actor=w.oid and c.oid=m.oid;
select * from comm c, worker w, shoutout s where c.actor=w.oid and c.oid=s.oid;

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

Я предлагаю вам использовать LINQ для PHP. Я знаю о .Net LINQ, но думаю, стоит попробовать PHPLinq.

http://phplinq.codeplex.com/

Вероятно, стоит потратить время на ознакомление с ролевым моделированием объектов, обсуждаемым в этом вопросе . Самая большая проблема, которую я вижу, заключается в том, что не существует общепринятой методологии обсуждения концептуального дизайна реляционных данных. Лучшее, что вы можете сделать, - это логическое моделирование (обычно это ERM). Основу для этого обсуждения составляет ролевое моделирование объектов. Надеюсь, вы увидите узнаваемые артефакты из аналогичного обсуждения дизайна ООП, которое у вас могло быть.

Это называется ORM или объектно-реляционное сопоставление . Есть десятки продуктов, которые призваны помочь вам сопоставить объектно-ориентированные структуры с реляционными таблицами. Ruby on Rails, например, предлагает Active Record, чтобы помочь преодолеть разрыв. Для PHP у вас есть Propel и Doctrine и Порт и многие другие.

Предлагаемый вами подход кажется мне оправданным. Вы можете добавить столбец actortype в свою базовую таблицу акторов, чтобы различать разные типы актеров. PK каждой конкретной таблицы акторов будет FK для таблицы акторов, чтобы избежать «волосатых» запросов и имитировать отношения типа наследования «is-a».

лучший ответ, который я когда-либо видел, был: http://en.wikipedia.org/wiki/The_Third_Manifesto

К сожалению, это не то, что умещается в пространстве единственного ответа здесь, в stackoverflow. Я попытаюсь сократить его здесь, но предупреждаю, что такое сокращение не будет точным отражением третьего манифеста. Пожалуйста, перенаправьте всю критику этого решения на собственное прочтение этой чертовой вещи, вместо того, чтобы предполагать, что вы полностью ее поняли, прочитав аббревиатуру. Хорошо, вот оно.

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

Мне кажется, что современные популярные технологии баз данных на самом деле не поддерживают «правильный» способ делать эти вещи (в частности, многие системы баз данных не позволяют определять новые типы). поэтому неважно, что вы делаете, вы всегда будете вынуждены оказаться в компромиссной ситуации. Но, прочитав третий манифест, по крайней мере, вы поймете, в чем идете на компромисс.

ORM в настоящее время является чрезвычайно популярным решением проблемы, но я не верю, что это правильное решение.

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

Кроме того, большинство СУБД имеют некоторую комбинацию триггеров, хранимых представлений и хранимых процедур, которые могут отделить поведение от реализации. Во многих случаях, например правила PostgreSQL (обобщение представлений), предлагают очень сложную инкапсуляцию и довольно просты в использовании.

.. Речь идет о том, "как безболезненно сопоставить структуры ООП с таблицами базы данных".

Вы этого не сделаете.

Объектно-ориентированная и реляционная алгебра - две принципиально разные парадигмы. Вы не можете переходить между ними без субъективной интерпретации. Это называется рассогласованием импеданса и получило название Вьетнам компьютерных наук .

Пара человек отметили несоответствие объектно-реляционного импеданса. Лучшее решение - просто отказаться от РСУБД в пользу ООСУБД , которая недавно вернула себе популярность.

Тем не менее, насколько мне известно, на чистом PHP нет объектных баз данных с API. Быстрый поиск дал такой результат, но он не обновлялся годами. С другой стороны, я слышал о множестве объектных баз данных для других языков, включая Hibernate , db4o и ZODB .

То, что вы ищете, это Disjoint-subtypes ... ORM - это хак.

Мне просто кажется, что в вашей модели данных отсутствует уровень. Я бы настроил это примерно так:

Люди Таблица - (Просто информация о реальных людях)

Таблица ролей - (типы ролей, которые могут быть у людей, например, работник, работодатель, контакт - и информация, относящаяся к этой роли)

Таблица PeopleRoles - (people_id, role_id, возможно, даты начала / изменения и т. Д.)

Таблица сущностей - (определение различных типов сущностей)

Таблица RoleEntities - (role_id, entity_id и т. Д.)

Тогда изменение человека с одной роли на другую (или разрешение им иметь несколько ролей) - это простое обновление.