Когда я должен использовать Perl DBIx :: Class?


17

DBIx :: Class - это популярный Perl-интерфейс для любой базы данных, к которой вы можете подключиться через DBI . Есть хорошая документация для технических деталей, но скудная информация о ее правильном использовании (включая ситуации, когда вы, вероятно, не хотите этого).

Во многих ситуациях люди рефлексивно достигают этого, потому что считают, что должны использовать его для всего, что связано с базой данных. Тем не менее, я чаще всего видел, что он неправильно используется до такой степени, что становится болевым пунктом. Мой вопрос во время обзоров кода и архитектуры всегда звучит так: «Какую пользу дает вам Foo?» Чаще всего разработчики, которых я вижу в этих ситуациях, не могут сформировать согласованный ответ на этот вопрос. Но они также часто не понимают простой SQL.

Последние пару месяцев я спрашивал людей: «Почему вы используете DBIx :: Class?» и получили только один хороший ответ (и этот человек также смог ответить на ответ «Когда бы вы его не использовали»). Питер Раббитсон, ведущий разработчик, в своем интервью еженедельнику FLOSS приблизился к ответу , но он немного утопает в середине интервью.

Итак, как я могу решить, подходит ли использование DBIx :: Class для проекта?


3
Ты пишешь другую книгу? :)
simbabque

1
По моему опыту, боль возникает, когда DBIC используется в ситуациях, когда это излишне. И, несмотря на то, что он очень мощный, он часто бывает излишним, потому что люди используют только базовые функции (генерация SQL и объединения) и больше ничего не нужны. Вот почему я написал DBIx :: Lite, который предоставляет эти базовые функции и не требует жестко запрограммированной схемы.
Алессандро

Ответы:


24

Прежде чем ответить на вопрос, я думаю, что некоторые фон в порядке.

Суть проблемы

После нескольких лет интервьюирования и найма разработчиков я узнал две вещи:

  1. Подавляющее большинство разработчиков имеют очень небольшой опыт проектирования баз данных.

  2. Я заметил слабую корреляцию между теми, кто не понимает базы данных, и теми, кто ненавидит ORM.

(Примечание: и да, я знаю, что есть те, кто очень хорошо разбирается в базах данных и ненавидят ORM)

Когда люди не понимают , почему внешние ключи важны, почему вы не встраивать имя производителя в itemтаблице, или почему customer.address1, customer.address2и customer.address3поля не является хорошей идеей, добавив ОРМ , чтобы сделать его проще для них ошибок базы данных записи ничем не поможет

Вместо этого, с правильно разработанной базой данных и сценарием использования OLTP, ORM являются золотыми. Большая часть основной работы уходит, и с помощью таких инструментов, как DBIx :: Class :: Schema :: Loader , я могу перейти от хорошей схемы базы данных к работе с кодом Perl за считанные минуты. Я бы сослался на правило Парето и сказал, что 80% моих проблем были решены с помощью 20% работы, но на самом деле я считаю, что выгоды даже больше, чем это.

Злоупотребление решением

Другая причина, по которой некоторые люди ненавидят ORM, заключается в том, что они пропускают абстракцию. Давайте рассмотрим общий случай веб-приложений MVC. Вот что мы обычно видим (псевдокод):

GET '/countries/offices/$company' => sub {
    my ( $app, $company_slug ) = @_;
    my $company = $app->model('Company')->find({ slug => $company_slug }) 
      or $app->redirect('/');
    my $countries = $app->model('Countries')->search(
     {
         'company.company_id' => $company->company_id,
     },
     {
         join     => [ offices => 'company' ],
         order_by => 'me.name',
     },
   );
   $app->stash({
     company   => $company,
     countries => $country,
   });
}

Люди пишут такие маршруты контроллеров и похлопывают себя по спине, думая, что это хороший, чистый код. Они были бы ошеломлены жестким кодированием SQL в своих контроллерах, но они сделали чуть больше, чем выставили другой синтаксис SQL. Их код ORM должен быть помещен в модель, и тогда они могут сделать это:

GET '/countries/offices/$company' => sub {
   my ( $app, $company_slug ) = @_;
   my $result = $app->model('Company')->countries($company_slug)
     or $app->redirect('/');
   $app->stash({ result => $result });
}

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

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

Отчетность и другие ограничения

Как пояснил Роб Киньон выше, отчетность обычно является слабым местом в ORM. Это подмножество более крупной проблемы, когда сложный SQL или SQL, который охватывает несколько таблиц, иногда не работает с ORM. Например, иногда ORM вызывает тип соединения, который мне не нужен, и я не могу сказать, как это исправить. Или, может быть, я хочу использовать подсказку индекса в MySQL, но это не так просто . Или иногда SQL просто настолько сложен, что было бы лучше написать SQL, а не предоставленную абстракцию.

Это одна из причин, по которой я начал писать DBIx :: Class :: Report . Пока он работает хорошо и решает большинство проблем, возникающих у людей (если они в порядке с интерфейсом только для чтения). И хотя на самом деле это похоже на костыль, если вы не пропускаете абстракцию (как объяснялось в предыдущем разделе), это делает работу с ней DBIx::Classеще проще.

Так, когда я выбрал бы DBIx :: Class?

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

Ключ к DBIx::Classправильному использованию такой же, как и для большинства ORM:

  1. Не пропускайте абстракцию.

  2. Напишите свои проклятые тесты.

  3. Знать, как перейти к SQL, при необходимости.

  4. Узнайте, как нормализовать базу данных.

DBIx::ClassПосле того, как вы изучите это, вы позаботитесь о большей части своей тяжелой работы и быстро создадите приложения.


1
Может быть, вы можете добавить еще один список, когда вы не будете его использовать. :)
Брайан Д. Фой

1
Это очевидно для вас, но, вероятно, не очевидно для многих читателей (я говорю, проведя годы в #dbix-classи #catalyst) - ключ к биту «не пропускай абстракцию» заключается в том, что каждая вещь, с которой вы работаете в DBIC, подкласс чего-то, что обеспечивает поведение резака печенья. Мы настоятельно рекомендуем добавлять методы в ваши подклассы, и, если вы не выполняете работу над вопросами и ответами, только методы, которые вы написали, должны быть частью вашего открытого интерфейса.
Хоббс

@hobbs: Действительно, именно здесь я вижу, что люди больше всего ошибаются в этом, и именно так они зацикливаются на DBIC. Мы часто предполагаем, что люди знают, что они делают в малом, но узнают, что в целом они не знают.
Брайан д Фой

9

Чтобы знать, когда использовать что-то, важно понимать, какова цель этой вещи. Какова цель продукта.

DBIx :: Class является ORM - объектно-реляционным картографом. ORM берет структуры данных реляционной базы данных на основе множеств и сопоставляет их с деревом объектов. Традиционное отображение - один объект на строку, использующий структуру таблицы в качестве описания класса. Родительско-дочерние отношения в базе данных рассматриваются как содержащие отношения между объектами.

Это кости этого. Но это не поможет вам решить, следует ли вам использовать ORM. ORM в первую очередь полезны, когда выполняются следующие условия:

  • Вы используете реляционную базу данных.
  • Ваши данные в основном используются для OLTP (обработка онлайн-транзакций).
  • Вы не пишете отчеты в вашем приложении.

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

Хотя ORM очень хороши в написании SQL для извлечения строк, они очень слабы в написании сопоставляющего SQL. Это тип SQL, на котором построены отчеты. Этот вид сортировки создается с использованием различных инструментов, а не ORM.

Существует много ORM на разных языках, несколько на Perl. Другие средства отображения - Class :: DBI и Rose :: DB. DBIx :: Class часто считается лучше других в значительной степени благодаря его наборам результатов. Это концепция, в которой генерация SQL отделена от выполнения SQL.


Обновление : в ответ на Ovid DBIx :: Class (через SQL :: Abstract) предоставляет возможность указать, какие столбцы возвращать, а также какие подсказки индекса использовать.

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

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

  • Агрегирующие запросы - это сценарии использования, для которых НЕ предназначены ORM . Хотя DBIx :: Class может создавать агрегирующие запросы, вы не создаете граф объектов, поэтому просто используйте DBI напрямую.
  • Оптимизация производительности используется потому, что запрашиваемые данные слишком велики для базовой базы данных, независимо от того, как вы к ней обращаетесь. Большинство реляционных баз данных идеально подходят для таблиц со строками длиной до 1-3 ММ, работающих на SSD, где большая часть данных + индексы помещается в ОЗУ. Если ваша ситуация больше, чем эта, то у каждой реляционной базы данных будут проблемы.

Да, отличный администратор баз данных может создавать таблицы с более чем 100-миллиметровой строкой в ​​Oracle или SQL * Server. Если вы читаете это, у вас нет отличного администратора в штате.

Все это говорит о том, что хороший ORM делает больше, чем просто создает графы объектов - он также предоставляет интроспективное определение вашей базы данных. Вы можете использовать это для создания специальных запросов и их использования, как при работе с DBI, без создания графа объектов.


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

1
Я не понимаю, почему вам нужно перейти к ручным запросам для отчетов. Я построил несколько довольно сложных отчетов с использованием DBIC. Конечно, это часто включает создание массивного настраиваемого набора результатов с интенсивным использованием «prefetch» ​​и «join».
Дейв Кросс

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

> чтобы убедиться, что вы извлекаете только те семь полей, которые вам нужны Да, это то, для чего используются атрибуты "columns" для поиска ResultSet. Единственные действительные аргументы, которые я слышал или видел для выполнения необработанного SQL, это: 1. Очень сложные подзапросы, которые обычно являются результатом плохо спроектированной таблицы / БД. 2. Выполнение DDL или других операций 'do', которых нет в DBIC. действительно построен для. 3. Попытка взломать индексные подсказки. Опять же, это скорее слабость СУРБД, но иногда это необходимо сделать. И можно просто добавить такую ​​функциональность в DBIC.
Брендан Берд

8

Как один из основных разработчиков платформы электронной торговли Interchange6 (и основной цепной пилы схемы), я имею довольно глубокий опыт работы с DBIC. Вот несколько вещей, которые делают его такой замечательной платформой:

  • Генератор запросов позволяет вам написать один раз для многих механизмов баз данных (и нескольких версий каждого). В настоящее время мы поддерживаем Interchange6 с MySQL, PostgreSQL и SQLite и добавим поддержку большего количества движков, как только у нас будет время и ресурсы. В настоящее время во всем проекте есть только два пути кода, которые имеют дополнительный код для учета различий между механизмами, и это происходит либо из-за отсутствия конкретной функции базы данных (SQLite местами не хватает), либо из-за идиотизма MySQL, который изменился способ, которым его функция LEAST обрабатывает NULL между двумя второстепенными версиями.

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

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

  • Schema :: Loader позволила нам использовать DBIC с унаследованными приложениями, что дает новую жизнь и намного упрощает путь в будущее.

  • Плагины DBIC, DeploymentHandler и Migration - все это чрезвычайно расширяет набор инструментов, которые делают мою жизнь проще.

Одно из огромных различий между DBIC и большинством других ORM / ORM-подобных платформ заключается в том, что, хотя оно пытается помочь вам в его действиях, оно также не мешает вам делать какие-то безумные вещи, которые вам нравятся:

  • Вы можете использовать функции SQL и хранимые процедуры, которые DBIC не знает, просто указав имя функции в качестве ключа в запросе (это также может доставить удовольствие при попытке использовать LEAST в MySQL ^^, но это не ошибка DBIC) ,

  • Литеральный SQL может использоваться, когда нет «способа DBIC», чтобы сделать что-то, и возвращенный результат все еще обернут в хорошие классы со средствами доступа.

TL; DR Я, вероятно, не стал бы использовать его для действительно простых приложений с парой таблиц, но когда мне нужно управлять чем-то более сложным, особенно когда межсистемная совместимость и долговременное обслуживание являются ключевыми, тогда DBIC Вообще мой любимый путь.


7

(Перед тем, как начать, я должен сказать, что это просто сравнение DBIC, DBI и оболочек DBI на основе Mojo. У меня нет опыта работы с любым другим Perl ORM, поэтому я не буду комментировать их напрямую).

DBIC делает много вещей очень хорошо. Я не большой пользователь этого, но я знаю теорию. Он неплохо справляется с генерацией SQL и, особенно (как мне сказали), обрабатывает объединения и т. Д. Он также может выполнять предварительную выборку других связанных данных.

Основным преимуществом, как я вижу, является возможность ПРЯМОГО использования результатов в качестве класса модели. Это также известно как «добавление методов набора результатов», где вы можете получить свои результаты и вызвать методы для этих результатов. Типичным примером является выборка пользовательского объекта из DBIC, а затем вызов метода для проверки правильности их пароля.

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

На другой стороне медали есть другие инструменты, которые обращаются к другим чувствительным факторам, такие как обертки DBI со вкусом mojo. У них есть привлекательность быть скудными и все же пригодными для использования. Большинство из них также воспользовались Mojo :: Pg (оригинал) и добавили удобные функции, такие как управление схемами в плоских файлах и интеграция с pubsub.

Эти специализированные модули Mojo выросли из еще одного слабого места DBIC, заключающегося в том, что он (пока) не способен выполнять асинхронные запросы. Авторы заверили меня, что это технически возможно, возможно, даже быстро, но есть проблемы при разработке API, который бы подходил. (По общему признанию меня даже попросили помочь с этим, и в то время как я буду, я просто не знаю, как переместить иглу в то время, которое я должен посвятить этому).

TL; DR используйте DBIC, если вы не любите SQL или вам не нужна асинхронность, и в этом случае исследуйте обертки DBI со вкусом Mojo.


6

Я написал свои мысли по этому поводу в DBIC против DBI три года назад. Подводя итог, я перечислил две основные причины:

  1. DBIC означает, что мне не нужно думать обо всем тривиальном SQL, который необходим практически для любого приложения, которое я пишу.
  2. DBIC возвращает мне объекты из базы данных, а не тупые структуры данных. Это означает, что у меня есть все стандартные ОО совершенства, чтобы играть. В частности, я считаю действительно полезным добавлять методы к моим объектам DBIC.

Что касается анти-паттернов, я могу думать только о производительности. Если вы действительно хотите выжать каждый такт из своего ЦП, то, возможно, DBIC не является подходящим инструментом для работы. Но, конечно, для приложений, которые пишут, такие случаи становятся все более редкими. Я не могу вспомнить, когда в последний раз я писал новое приложение, которое общалось с базой данных и не использовал DBIC. Конечно , полезно, если вы немного знаете о настройке запросов, которые генерирует DBIC.


2
Да, я не могу исправить опечатки, потому что я не меняю достаточно символов ("Right Ttool"). Любопытно хромой. Но такой ответ меня озадачивает. Я думаю, что в вашем посте PerlHacks вы упоминаете одну вещь, на которую указывает Роб, но не учитываете другую. Во многих случаях я обнаружил, что люди возвращаются к ручному SQL.
Брайан Д. Фой

1

То, как я делаю это масштаб:

  1. создайте один класс, который предоставляет конструктор сокета DBI и методы тестирования.

  2. Извлеките этот класс в ваши классы запросов SQL (один класс на таблицу SQL) и протестируйте сокет во время конструктора.

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

  4. Запишите все ваши интерполирующие имя таблицы SQL и столбец первичного индекса из этих переменных вместо того, чтобы определять их в SQL статически.

  5. используйте макросы редактора, чтобы позволить вам создавать базовые пары методов DBI (подготовить и выполнить), вводя ТОЛЬКО оператор SQL.

Если вы можете сделать это, вы можете писать чистый код API поверх DBI весь день с относительной легкостью.

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

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

Когда использовать DBI: всегда.

Я не думаю, что это был твой настоящий вопрос. Ваш реальный вопрос был: «Это похоже на огромную PITA, пожалуйста, скажите мне, что я не должен это делать?»

Вы не Выложите все правильно, и часть DBI станет достаточно избыточной, чтобы иметь возможность в основном автоматизировать ее.


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

0

Я не эксперт по Perl, но я им часто пользуюсь. Есть много вещей, которые я не знаю или не знаю, как сделать лучше; некоторые вещи, которые я просто еще не в состоянии понять, несмотря на документацию.

Я склонен начинать с DBI, потому что думаю: «О, это простой проект, мне не нужно раздувать ORM, и я не хочу хлопотать с модулями setup и Schema». Но очень быстро - почти каждый раз - я быстро начинаю проклинать себя за это решение. Когда я хочу начать проявлять креативность в своих SQL-запросах (динамических запросах, а не только заполнителях сравнения), я стараюсь поддерживать здравый смысл, используя DBI. SQL :: Abstract очень помогает, и, как правило, этого мне достаточно. Но моя следующая ментальная борьба - поддерживать так много SQL внутри моего кода. Мне очень неприятно иметь строки и строки встроенного SQL внутри уродливых heredocs. Может быть, мне нужно использовать качественную IDE.

В конце концов, чаще, чем нет, я придерживаюсь прямого DBI. Но я продолжаю желать, чтобы был лучший способ. DBIx :: Class обладает некоторыми действительно хорошими характеристиками, и я использовал его несколько раз, но он кажется слишком излишним для всех, кроме самых крупных проектов. Я даже не уверен, что мне кажется более трудоемким в управлении: DBI с разбросанным SQL или DBIC с разбросанными модулями Schema.

(О, такие вещи как ограничения и триггеры - огромный плюс для меня для DBIC.)


4
Этот ответ не доходит до сути - конечно, у вас есть проблемы при использовании DBIx, но почему у вас есть эти проблемы? Не является ли DBI гибким, слишком тесно связанным с БД, не масштабируемым или как?
Джей Элстон
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.