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

Folks,

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

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

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

Актеры:

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

Объекты:

  • связь
  • заметки
  • и т.п.

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

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

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

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

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

11 ответов

Решение

Вот решение, которое я нашел около 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;

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

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

Вы не

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

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

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

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

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

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

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

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

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

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

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

http://phplinq.codeplex.com/

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

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

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

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

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

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

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

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

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

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

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

Другие вопросы по тегам