Вот решение, которое я нашел около 10 лет назад. Система, которая использует этот дизайн, все еще работает, поэтому она работала достаточно хорошо, чтобы выжить дольше, чем большая часть моего кода. ;) Сегодня я могу использовать один из пакетов ORM, о котором упоминает Скотт, но на самом деле больших проблем не возникает, если использовать SQL напрямую.
Смоделируйте все ваши отношения наследования как объединения между таблицами. Каждая таблица в вашей системе будет содержать атрибуты определенного класса.
Используйте синтетический идентификатор объекта (oid) в качестве первичного ключа для всех объектов. Генератор последовательности или автоинкрементный столбец необходимы для генерации значений oid.
Все унаследованные классы должны использовать тот же тип oid, что и их родительский класс. Определите oid как внешний ключ с каскадным удалением. Родительская таблица получает столбец oid автоинкремента, а дочерние - простые столбцы oid.
Запросы на выпускные классы производятся по соответствующей таблице. Вы можете либо объединить все родительские таблицы классов в запросе, либо просто лениво загрузить нужные вам атрибуты. Если ваша иерархия наследования глубока и у вас много классов, пакет ORM действительно может упростить ваш код. В моей системе было менее 50 классов с максимальной глубиной наследования 3.
Запросы к дочерним классам (то есть запросы к родительскому классу) могут либо лениво загружать дочерние атрибуты для каждого экземпляра, либо вы можете повторить запрос для каждого дочернего класса, объединенного с базовыми классами. Ленивая загрузка дочерних атрибутов на основе запроса родительского класса требует, чтобы вы знали тип объекта. Возможно, у вас уже достаточно информации в родительских классах, но если нет, вам нужно будет добавить информацию о типе. Опять же, здесь может помочь пакет 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;
И последнее. Убедитесь, что у вас есть хорошая база данных и правильные индексы. Производительность может быть серьезной проблемой, если ваша база данных не может оптимизировать эти объединения.