Лучший шаблон проектирования для объединения таблиц базы данных - PullRequest
18 голосов
/ 23 октября 2009

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

Например, предположим, что у вас есть следующие таблицы / отношения: Клиент -> 1..n Счета -> 0..n Особенности

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

Затем я хочу выполнить getCustomersForFeature (), чтобы получить всех клиентов с учетными записями, которые имеют бесплатную туристическую страховку.

Использование ActiveRecord или Data Access Object, кажется, не подходит, так как они обычно фокусируются на одном классе на таблицу; аналогично для Data Mapper. Я понимаю, что могу разбить его на операции для каждой таблицы, например, getAccountsForFeature () и getCustomersForAccount (), но я хочу выполнить поиск одним нажатием.

Если бы нам пришлось «согнуть» шаблоны по одному классу на таблицу и использовать шаблон объекта доступа к данным, скажем, должен ли метод getCustomersForFeature () пойти на CustomerDAO или FeatureDAO? Но мне это не кажется правильным, потому что вы будете загрязнять свои DAO знаниями других таблиц.

Предложения, пожалуйста.

Ответы [ 9 ]

9 голосов
/ 29 октября 2009

В Model-Driven Design в вашем приложении есть логические сущности , и эти сущности могут отображаться в несколько таблиц в физической базе данных. Конечно, вам нужно запустить более сложный SQL для извлечения полной сущности, и класс Model - это место, где реализуются эти отношения.

Я думаю, что ActiveRecord и тому подобное можно использовать для простых запросов к одной таблице, но пытаться принудительно использовать эти шаблоны для сложных запросов слишком сложно. К счастью, у нас уже есть краткий, предметно-ориентированный язык, который вы можете использовать для определения сложных запросов: SQL.

Таким образом, в вашем классе Model у вас есть методы для выполнения логических задач уровня приложения, например getCustomersForFeature(). В коде для этого метода вы должны написать конкретный запрос, либо с помощью методов ActiveRecord, либо с прямым SQL, если это необходимо. Таким образом, конкретный дизайн вашей базы данных инкапсулирован в одном месте, в классе Model.

Это означает, что нам нужно разорвать связь между моделью и таблицей. Отношения ОО между Моделью и классом ActiveRecord не IS-A - это HAS-A (или has-many).


Re ваш комментарий: Так что наша модель? Если вашему приложению в первую очередь необходимо работать с клиентами как единым целым и рассматривать функции как более или менее атрибут клиентов, то да, ваша Модель будет Покупателями, и это будет скрывать тот факт, что функции хранятся в отдельной таблице в базе данных. , Модель Customers будет внутренне использовать ActiveRecord или обычный SQL для сбора необходимых данных, чтобы обеспечить полное представление о сложном объекте, являющемся клиентом, со связанными многозначными атрибутами.

Однако, что если вашему приложению также необходимо напрямую работать с функциями? Например, экран администратора, где вы можете получать отчеты на основе функций или создавать новые функции. Тогда было бы неуклюже получить доступ к функциям через модель Customer. Таким образом, вам все-таки нужна модель Особенности. Только у него будут разные методы для реализации операций, которые вам нужно делать с функциями.

Каждый класс модели должен предоставлять API только того, что нужно сделать с этой моделью . Там нет необходимости даже быть симметричным. То, что ваша модель Customer может выбирать всех клиентов, у которых есть данная функция, не обязательно означает, что вашей модели Feature необходимо получить все функции для данного клиента. Следуйте правилу YAGNI .

Но после того, как вы создали свою модель Customer и свою модель Feature, не приведет ли это к дублированию логики, которая знает об отношениях между таблицами? Да, это возможно. Это одна из многих проблем, подпадающих под область несоответствия объектно-реляционного импеданса .

3 голосов
/ 01 ноября 2009

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

Я также еще раз взглянул на свою копию Patterns of Enterprise Application Architecture, и, хотя кажется, что шаблон ActiveRecord предназначен для прямого отображения класса в таблицу базы данных, если вы решили не следовать подходу Repository из DDD, тогда Data Mapper подходит для составного отображения.

Спасибо всем за ваш вклад. Я проголосовал за ответы, которые способствовали этому выводу. Поскольку это может быть не последним пунктом в этом обсуждении, я пометил этот ответ как Вики сообщества.

2 голосов
/ 23 октября 2009

Способ, используемый Active Record в Rails, моделирует это, позволяя объекту Customer иметь учетные записи have_many, которые в основном преобразуются в набор объектов Account, которые, в свою очередь, имеют набор функций. Отношения могут быть двунаправленными, поэтому каждая модель AR может «знать» о своих отношениях в зависимости от ваших потребностей.

Я думаю, что для объекта хорошо знать другие таблицы, поскольку такие отношения фундаментальны как для OO, так и для RDBMS / SQL.

1 голос
/ 23 октября 2009

В C # / Linq to SQL это будет что-то вроде следующего. (Я предполагаю, что на самом деле есть справочная таблица типов объектов, поэтому у вас есть стандартный список типов объектов, а затем их связь с учетной записью будет отдельной, поэтому FeatureTypeId будет вашим значением FK, возможно, выбранным из раскрывающегося списка. список или что-то.)

// or whatever data type your keys are in
public IEnumerable<Customer> getCustomersForFeature(int featureTypeId)
{
    return from feature in dbContext.Features
           where feature.FeatureTypeId == featureTypeId
           select getCustomer(feature.Account.Customer.Id);
}

На каком-то уровне ваши DAO / BAO должны знать об отношениях между вашими объектами, поэтому тот факт, что это отношения прародителя, не должен быть слишком страшным.

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

Редактировать: как указал Тоби, отношения являются двунаправленными; снова в Linq, вы можете пойти и другим путем:

// or whatever data type your keys are in
public IEnumerable<Customer> getCustomersForFeature(int featureTypeId)
{
    return from customer in dbContext.Customers
           from account in customer.Accounts
           from feature in account.Features
           where feature.FeatureTypeId == featureTypeId
           select getCustomer(customer.Id);
}

Любой другой ORM должен иметь очень похожее поведение, даже если синтаксис и структура будут различаться.

0 голосов
/ 30 октября 2009

Требования решают за вас. Либо по вашему сроку, вашей рабочей культуре или вашим требованиям проекта. Ваш крайний срок может указывать, «код для инструментов» и вырезать функции. Вашему персоналу может потребоваться «нет новых библиотек или режимов сбоев». В моем собственном окружении вы услышите, как я рассказываю эту историю:

"... не нужно было бы вводить новые библиотеки ORM, так что их уже нет. Сроки указывают на то, что я не должен начинать учить себя тому, как создавать представления SQL. Моя рабочая культура информирует меня, чтобы получить это сделано быстро. Моя архитектура стонет при сериализации целых таблиц и мои требования к производительности указывают, что я не должен кэшировать эти данные ... "

Представления SQL могут обеспечить хороший способ абстрагирования соединений от ваших объектов и могут позволить вам изменять свою схему более независимо от кода вашего приложения. Обновление просмотров очень сложно, хотя, в зависимости от вашей базы данных. Если переносимость БД важна, это, вероятно, очень важно для вашего дизайна.

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

0 голосов
/ 29 октября 2009

Несмотря на то, что Object to realtional отлично подходит для ведения таблиц типов CRUD, я не вижу ничего лучше реального SQL, когда речь идет о таблицах сложных запросов.

Модель ORM - просто абстракция слишком далеко.

0 голосов
/ 29 октября 2009

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

А что касается вашего высказывания ' Я хочу выполнить поиск одним нажатием. ', я думаю, вам следует написать собственный SQL-запрос или, возможно, использовать LINQ.

0 голосов
/ 23 октября 2009

Согласился с GalacticCowboy, что ORM позволяет отделить детали отображения классов базы данных от объектно-ориентированных запросов. Просто хочу привести пример для java hibernate orm - есть несколько способов определить сопоставления ассоциаций , и следующий объектно-ориентированный запрос HQL отлично работает над всеми из них

выберите c из Клиента c левым соединением c.ccounts левого присоединения a.features f где f.name = 'cool'

Обратите внимание, что 'Customer' является здесь именем класса, а 'c.accounts' , ' a.features' и 'f. name ' являются именами свойств уровня класса, т.е. здесь не упоминаются подробности, относящиеся к базе данных.

0 голосов
/ 23 октября 2009

В целом смоделируйте свои данные в соответствии с возможностями используемой вами базы данных SQL. Игнорировать ORM, Mappers и т. Д.

Если у вас есть хорошая модель данных, если ActiveRecord или DAO не будут обращаться к ней так, как вам нужно, то обойдите их, написав SQL.

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

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