Объектно-ориентированные структуры в реляционных базах данных - PullRequest
9 голосов
/ 02 марта 2009

Народ,

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

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

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

Актеры:

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

Сущность:

  • примечания
  • и т.д.

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

  • рабоче-коммуникационно-Assn
  • работодатель-коммуникационно-Assn
  • рабочие струны-Assn
  • и т. Д., Вы получаете упражнение.

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

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

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

Ответы [ 11 ]

8 голосов
/ 02 марта 2009

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

Вы не.

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

7 голосов
/ 02 марта 2009

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

6 голосов
/ 02 марта 2009

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

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

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

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

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

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

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

Вот как выглядит "покажи мне все связи с актерами типа работник".

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;

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

3 голосов
/ 02 марта 2009

То, что вы ищете, это непересекающиеся подтипы ... ORM - это хак.

3 голосов
/ 02 марта 2009

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

2 голосов
/ 02 марта 2009

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

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

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

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

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

1 голос
/ 02 марта 2009

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

0 голосов
/ 14 декабря 2010

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

http://phplinq.codeplex.com/

0 голосов
/ 02 марта 2009

Пара человек заметили несоответствие объектно-реляционного импеданса. Лучшее решение - просто отказаться от RDBMS в пользу OODBMS , которая недавно приобрела популярность.

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

0 голосов
/ 02 марта 2009

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

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

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