9 ответов:
существует несколько способов моделирования наследования в базе данных. Что вы выбираете зависит от ваших потребностей. Вот несколько вариантов:
Table-Per-Type (TPT)
каждый класс имеет свою собственную таблицу. Базовый класс имеет все элементы базового класса в нем, и каждый класс, производный от него, имеет свою собственную таблицу с первичным ключом, который также является внешним ключом к таблице базового класса; класс производной таблицы содержит только различные элементы.
например:
class Person { public int ID; public string FirstName; public string LastName; } class Employee : Person { public DateTime StartDate; }приведет к таким таблицам, как:
table Person ------------ int id (PK) string firstname string lastname table Employee -------------- int id (PK, FK) datetime startdateтаблица на иерархию (TPH)
существует одна таблица, которая представляет всю иерархию наследования, что означает, что несколько столбцов, вероятно, будут разреженными. Добавляется столбец дискриминатора, который сообщает системе, какой тип строки это.
учитывая классы выше, вы в конечном итоге с этой таблицей:
table Person ------------ int id (PK) int rowtype (0 = "Person", 1 = "Employee") string firstname string lastname datetime startdateдля любые строки, которые являются rowtype 0 (Person), startdate всегда будет null.
таблица на бетон (TPC)
каждый класс имеет свою собственную полностью сформированную таблицу без ссылок на любые другие таблицы.
учитывая классами выше, вы в конечном итоге с этими таблицами:
table Person ------------ int id (PK) string firstname string lastname table Employee -------------- int id (PK) string firstname string lastname datetime startdate
правильный дизайн базы данных-это не что иное, как правильный дизайн объекта.
Если вы планируете использовать базу данных для чего-либо, кроме простой сериализации объектов (таких как отчеты, запросы, использование нескольких приложений, бизнес-аналитика и т. д.).) тогда я не рекомендую какого-либо простого сопоставления объектов с таблицами.
многие люди думают о строке в таблице базы данных как о сущности (я провел много лет, думая в этих терминах), но строка не является сущностью. Это утверждение. Отношение базы данных (т. е. таблица) представляет собой некоторую констатацию факта о мире. Наличие строки указывает на то, что факт истинен (и наоборот, его отсутствие указывает на то, что факт ложен).
при таком понимании вы можете видеть, что один тип в объектно-ориентированной программе может храниться через дюжину различных отношений. И различных типов (объединяет наследование, ассоциация, агрегация, или полностью независимые) могут быть частично сохранены в одном отношении.
лучше всего спросить себя, какие факты вы хотите хранить, на какие вопросы вы хотите получить ответы, какие отчеты вы хотите создать.
после того, как правильный дизайн БД создан, то это простой вопрос, чтобы создать запросы/представления, которые позволяют сериализовать ваши объекты к этим отношениям.
пример:
в системе бронирования отелей вам может потребоваться сохранить тот факт, что Джейн Доу забронировала номер в отеле отель Seaview Inn на 10-12 апреля. Это атрибут сущности "клиент"? Является ли это атрибутом сущности отеля? Это объект бронирования со свойствами, которые включают клиента и отель? Это может быть любая или все эти вещи в объектно-ориентированной системе. В базе данных, то ни одна из этих вещей. Это просто голый факт.
чтобы увидеть разницу, рассмотрим следующие два запроса. (1) Сколько бронирований отелей у Джейн Доу на следующий год? (2) Сколько комнат забронировано на 10 апреля в Seaview Inn?
в объектно-ориентированной системе запрос (1) является атрибутом сущности customer, а запрос (2) - атрибутом сущности hotel. Это объекты, которые будут предоставлять эти свойства в своих API. (Хотя очевидно, что внутренние механизмы, с помощью которых эти значения получаются, могут включать ссылки на другие объекты.)
в системе реляционных баз данных оба запроса будут проверять отношение резервирования, чтобы получить их числа, и концептуально нет необходимости беспокоиться о какой-либо другой "сущности".
таким образом, при попытке хранить факты о мире-а не пытаться хранить сущности с атрибутами-создается правильная реляционная база данных. И как только он будет правильно спроектирован, тогда полезные запросы, о которых не было и мечтать на этапе проектирования, могут быть легко построены, поскольку все факты, необходимые для выполнения этих запросов, находятся на своих местах.
короткий ответ: нет.
Если вам нужно сериализовать свои объекты, используйте ORM или даже лучше что-то вроде activerecord или prevaylence.
Если вам нужно хранить данные, храните их реляционным образом (будьте осторожны с тем, что вы храните, и обращайте внимание на то, что только что сказал Джеффри л Уитледж), а не на ваш дизайн объекта.
шаблоны TPT, TPH и TPC-это способы, которыми вы идете, как упоминал Брэд Уилсон. Но пара заметок:
дочерние классы, наследуемые от базового класса, могут рассматриваться как слабые сущности для определения базового класса в базе данных, что означает, что они зависят от своего базового класса и не могут существовать без него. Я видел много раз, что уникальные идентификаторы хранятся для каждой дочерней таблицы, а также сохраняя FK в родительской таблице. Одного ФК как раз достаточно и свое еще лучше включить каскад on-delete для FK-отношения между дочерними и базовыми таблицами.
В TPT, только видя записи базовой таблицы, вы не можете найти, какой дочерний класс представляет запись. Иногда это необходимо, когда вы хотите загрузить список всех записей (без
selectна каждой дочерней таблице). Один из способов справиться с этим, чтобы иметь один столбец, представляющий тип класса (аналогично полю rowType в TPH), поэтому смешивание TPT и TPH каким-то образом.скажем, мы хотим создать базу данных, которая содержит следующую диаграмму классов формы:
public class Shape { int id; Color color; Thickness thickness; //other fields } public class Rectangle : Shape { Point topLeft; Point bottomRight; } public class Circle : Shape { Point center; int radius; }дизайн базы данных для вышеуказанных классов может быть следующим:
table Shape ----------- int id; (PK) int color; int thichkness; int rowType; (0 = Rectangle, 1 = Circle, 2 = ...) table Rectangle ---------- int ShapeID; (FK on delete cascade) int topLeftX; int topLeftY; int bottomRightX; int bottomRightY; table Circle ---------- int ShapeID; (FK on delete cascade) int centerX; int center; int radius;
существует два основных типа наследования, которые можно настроить в БД: таблица для сущности и таблица для иерархии.
таблица на сущность-это место, где у вас есть базовая таблица сущностей, которая имеет общие свойства всех дочерних классов. Затем у вас есть для каждого дочернего класса другая таблица, каждая из которых имеет только свойства, применимые к этому классу. Они связаны 1: 1 их ПК
alt текст http://mattlant.com/ent.png
таблица на иерархию, где все классы общая таблица и необязательные свойства могут быть обнулены. Их также является полем дискриминатора, которое представляет собой число, обозначающее тип, который в настоящее время содержит запись
alt текст http://mattlant.com/hier.png SessionTypeID является дискриминатором
цель на иерархию быстрее запрашивать, поскольку вам не нужны соединения (только значение дискриминатора), тогда как цель на сущность вам нужно сделать сложные соединения, чтобы определить, какой тип что-то есть, а также восстановите все свои данные..
Edit: изображения, которые я показываю здесь, - это снимки экрана проекта, над которым я работаю. Изображение актива не является полным, следовательно, пустота его, но это было в основном, чтобы показать, как его настройка, а не то, что положить внутри ваших таблиц. Это зависит от вас ;). Таблица сеансов содержит информацию о сеансах виртуальной совместной работы и может иметь несколько типов сеансов в зависимости от типа совместной работы.
вы бы нормализовали свою базу данных, и это фактически отразило бы ваше наследование. Это может иметь ухудшение производительности, но именно так обстоит дело с нормализацией. Вы, вероятно, придется использовать здравый смысл, чтобы найти баланс.
повторите аналогичный ответ потока
в O-R сопоставлении наследование сопоставляется с родительской таблицей, где родительская и дочерняя таблицы используют один и тот же идентификатор
create table Object ( Id int NOT NULL --primary key, auto-increment Name varchar(32) ) create table SubObject ( Id int NOT NULL --primary key and also foreign key to Object Description varchar(32) )подобъект имеет отношение внешнего ключа к объекту. при создании строки подобъекта необходимо сначала создать строку объекта и использовать идентификатор в обеих строках
EDIT: если вы хотите также моделировать поведение, вам понадобится таблица типов, в которой перечислены отношения наследования между таблицами и указано имя сборки и класса, которые реализовали поведение каждой таблицы
кажется излишним, но все зависит от того, для чего вы хотите его использовать!
используя SQL ALchemy (Python ORM), вы можете сделать два типа наследования.
тот, который у меня был опыт, использует таблицу паленого и имеет дискриминантный столбец. Например, база данных овец (не шутка!) хранили всех овец в одной таблице, а Баранов и овец обрабатывали с помощью столбца пола в этой таблице.
таким образом, вы можете запрашивать все овцы и бараны. Или вы можете запросить только ОЗУ, и он будет получать только ОЗУ. Вы также можете делать такие вещи, как иметь отношение, которое может быть только баран (т. е. родитель овцы), и так далее.
обратите внимание, что некоторые ядра СУБД уже предоставляют механизмы наследования изначально, как Postgres. Посмотрите на документация.
например, вы бы запросили систему Person / Employee, описанную в ответе выше, например:
/* This shows the first name of all persons or employees */ SELECT firstname FROM Person ; /* This shows the start date of all employees only */ SELECT startdate FROM Employee ;в этом выбор вашей базы данных, вам не нужно быть особенно умным !
Comments