Коли я повинен використовувати DBIx :: Class Perl?


17

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

У багатьох ситуаціях люди досягають цього рефлекторно, оскільки думають, що вони повинні використовувати його для всього, що стосується бази даних. Однак я найчастіше бачив, як його неправильно застосовують до того, коли він стає больовим. Моє запитання під час огляду коду та архітектури завжди "Яку користь дає Foo?" Найчастіше розробники, яких я бачу в таких ситуаціях, не можуть скласти цілісну відповідь на це. Але тоді вони також часто не розуміють простий SQL.

Останні пару місяців я запитував людей "Чому ви використовуєте DBIx :: Class?" і отримав лише одну хорошу відповідь (і ця людина також змогла відповісти на подальші дії "Коли ти не використаєш її"). Пітер Раббітсон, провідний розробник, наблизився до відповіді у своєму інтерв'ю на FLOSS Weekly , але це трохи поховано в середині інтерв'ю.

Отже, як я можу вирішити, чи використання DBIx :: Class підходить для проекту?


3
Ви пишете ще одну книгу? :)
simbabque

1
На мій досвід, біль виникає, коли DBIC використовується в ситуаціях, коли це надмірно. І, незважаючи на те, що він дуже потужний, часто це надмірно, тому що люди використовують лише основні функції (генерація SQL та приєднання) і нічого більше не потребують. Ось чому я написав DBIx :: Lite, який надає ті основні функції та не вимагає жорсткої кодування жодної схеми.
Алессандро

Відповіді:


24

Перш ніж відповісти на питання, я думаю, що деякий фон в порядку.

Основа проблеми

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

  1. Переважна більшість розробників мають дуже мало досвіду в розробці баз даних.

  2. Я помітив вільну зв'язок між тими , хто не розуміє , баз даних і тих , хто ненавидить ORMS.

(Примітка: і так, я знаю, що є ті, хто дуже добре розуміє бази даних, які ненавидять 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 :: Звіт . Поки це добре працює і вирішує більшість питань, які тут виникають (поки вони в порядку з інтерфейсом лише для читання). І хоча насправді це здається милицьким, насправді, поки ви не просочуєтесь абстракцією (як пояснено в попередньому розділі), це робить роботу DBIx::Classще простіше.

Отже, коли я б обрав DBIx :: Class?

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

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

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

  2. Пишіть свої прокляті тести.

  3. Знайдіть, як потрібно перейти до SQL.

  4. Дізнайтеся, як нормалізувати базу даних.

DBIx::Class, як тільки ви дізнаєтесь це, він подбає про більшість ваших важких підйомів для вас і зробить легким вітерцем для швидкого написання заявок.


1
Можливо, ви можете додати ще один список, коли ви не використовували б його. :)
Брайан d foy

1
Це очевидно для вас, але, мабуть, не очевидно для багатьох читачів (я кажу, витративши роки в #dbix-classі #catalyst) - ключ до біту "не протікайте абстракції" полягає в тому, що кожна річ, з якою ви працюєте в DBIC, підклас чогось, що забезпечує поведінку вирізання файлів cookie. Вам настійно рекомендується додати методи до своїх підкласів, і якщо ви не виконуєте Q&D, лише ті методи, які ви написали, повинні бути частиною вашого загальнодоступного інтерфейсу.
панночки

@hobbs: Дійсно, саме там я бачу, що люди найбільше неправильно ставляться до цього, і саме вони зациклюються на DBIC. Ми часто припускаємо, що люди знають, що вони роблять у малому, але у великому розуміють, що цього не роблять.
Брайан d foy

9

Щоб знати, коли щось використовувати, важливо зрозуміти, в чому полягає мета речі. Яка мета продукту.

DBIx :: Class - це ORM - об'єктно-реляційний Mapper. ORM приймає структури даних реляційних баз даних на основі реляції / набору і відображає їх у дерево об'єктів. Традиційне відображення - це один об'єкт на рядок, використовуючи структуру таблиці як опис класу. Відносини батько-дитина в базі даних розглядаються як містять зв'язки між об'єктами.

Ось це кістки. Але це не допоможе вам вирішити, чи варто використовувати ORM. ORM в першу чергу корисні, коли справедливо наступне:

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

Найбільша сила, яку має ОРМ, - це створення хорошого SQL для переходу на графік дерева, накладений поверх структури реляційних даних. SQL часто волохатий і складний, але це ціна управління невідповідністю імпедансу.

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

Є багато ОРМ на різних мовах, кілька - в Perl. Інші картографи - це клас :: DBI та Rose :: DB. DBIx :: Клас часто вважається кращим, ніж інші, значною мірою на основі набору результатів. Це концепція, де генерація SQL відокремлена від виконання SQL.


Оновлення : У відповідь на Овідій, DBIx :: Class (через SQL :: Abstract) надає можливість вказати і які стовпці для повернення, а також те , що індекс натяків для використання.

Взагалі, якщо ви хочете це зробити, то вам краще не використовувати ORM для цього конкретного запиту. Пам'ятайте - головне призначення ORM - зіставлення рядків у таблиці на об’єкти класу, атрибутами яких є стовпці таблиці. Якщо ви лише заповнюєте деякі атрибути, то потенційні користувачі цього об’єкта не дізнаються, які атрибути заповнені чи ні. Це призводить до жахливого оборонного програмування та / або загальної ненависті до ОРМ.

Практично завжди бажання використовувати підказки-покажчики або обмежувати повернення стовпців є оптимізацією для швидкості та / або сукупним запитом.

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

Так, великий DBA може скласти таблиці з функцією рядків 100MM + у Oracle або SQL * Server. Якщо ви читаєте це, у вас немає чудового базу даних персоналу.

З урахуванням всього цього, хороший ORM робить більше, ніж просто створює графіки об'єктів - він також надає інтроспективне визначення вашої бази даних. Ви можете використовувати це для створення спеціальних запитів та споживання їх, як у DBI, без створення об’єктного графіка.


Я думаю, що майже все, що я бачу, пише звіти, тому, мабуть, тому людям доводиться переходити до запитів вручну (і це біль).
Брайан d foy

1
Я не розумію, чому потрібно перейти на запити вручну для звітів. Я створив кілька досить складних звітів за допомогою DBIC. Звичайно, це часто передбачає побудову масивного набору індивідуальних результатів із великим використанням "prefetch" та "join".
Дейв Крос

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

> щоб переконатися, що ви тягнете лише сім полів, вам потрібно Так, саме для цього використовуються attr "стовпці" для пошуку ResultSet. Єдині справедливі аргументи, які я чув або бачив для виконання необробленого SQL, це: 1. Дуже складні підзапити, які зазвичай є продуктом погано розробленої таблиці / БД 2. Запуск DDL або інших операцій "робити", які не є DBIC Насправді не побудований. 3. Спроба зламати індекси. Знову ж таки, це більше слабкість RDBMS, але іноді це потрібно зробити. І можна просто додати такий тип функціональності в DBIC.
Брендан Берд

8

Як один з основних розробників платформи електронної комерції Interchange6 (і головна бензопила з основною схемою), я маю досить глибокий досвід роботи з DBIC. Ось кілька речей, які роблять його такою чудовою платформою:

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

  • Заздалегідь задані запити означають, що я можу створити прості методи, які можна викликати (з аргументами або без них) з коду програми, тому я зберігаю свої запити здебільшого всередині визначення схеми замість засмічення коду програми.

  • Компонентне генерування запитів дозволяє розбивати запити на невеликі, зрозумілі заздалегідь задані запити, а потім з’єднувати їх між собою, щоб створити складні запити, які були б важко підтримувати довгостроково в будь-якій DBIC (ще гірше в чистому SQL), якщо вони були побудовані за один крок.

  • Схема :: Навантажувач дозволив нам використовувати DBIC зі застарілими програмами, що дає нову оренду життя та набагато простіший шлях до майбутнього.

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

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

  • Ви можете використовувати функції SQL та збережені процедури, які DBIC не знає, лише надаючи ім'я функції як ключ у запиті (також може спричинити задоволення при спробі використання LEAST у MySQL ^^, але це не помилка DBIC) .

  • Literal SQL можна використовувати, коли немає "способу DBIC", щоб щось зробити, і результат, який повертається, все ще завершений у хороші класи з аксесуарами.

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


7

(Перш ніж почати, я повинен сказати, що це лише порівняння обгортки DBI, DBI та Mojo на основі Mojo. Я не маю досвіду роботи з будь-якими іншими Perl ORM, тому не буду коментувати їх безпосередньо).

DBIC робить багато речей дуже добре. Я не важкий його користувач, але я знаю теорію. Це дуже хороша робота покоління SQL і особливо (як мені вже сказали) обробка приєднується тощо. Це також може непогано виконати попереднє вилучення інших пов'язаних даних.

Головною перевагою, на мою думку, є можливість прямого використання результатів як модельного класу. Інакше це відомо як "додавання методів набору результатів", де ви можете отримати свої результати та викликати методи на цих результатах. Найпоширеніший приклад - це отримання об’єкта користувача з DBIC, а потім виклик методу, щоб перевірити, чи дійсний їх пароль.

Звичайно, розгортання схеми може бути важким, але це завжди важко. У DBIC є інструменти (деякі у зовнішніх модулях), які полегшують та, ймовірно, простіше, ніж керувати власними схемами вручну.

З іншого боку монети є інші інструменти, які приваблюють інші чутливості, як упаковки DBI з ароматом моджо. Вони мають привабливість бути худорлявими і все ще придатними для використання. Більшість також взяли підказку від 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
Так, я не можу виправити помилки, оскільки я не змінюю достатньо символів ("правда"). Цікаво кульгавий. Але це така відповідь, яка мене спантеличує. Я думаю, що у своєму дописі PerlHacks ви звертаєтесь до однієї речі, яку вказує Роб, але не розглядаєте іншої. У багатьох випадках я знайшов людей, які повертаються до посібника SQL.
Брайан d foy

1

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

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

  2. вивести цей клас у ваші класи запитів SQL (один клас на таблицю sql) та протестувати сокет на час конструктора.

  3. використовуйте змінні масштабування класу, щоб утримувати імена таблиці та назви стовпців основного індексу.

  4. Запишіть усе ім’я таблиці інтерполяції SQL та первинний стовпець індексу з цих змінних, а не визначайте їх у SQL статично.

  5. використовуйте макроси редактора, щоб дозволити вам робити основні пари методів DBI (готувати та виконувати), вводячи ТІЛЬКИ оператор sql.

Якщо ви можете це зробити, ви можете записувати чистий код API поверх DBI цілий день з відносною легкістю.

Що ви знайдете, це те, що багато ваших запитів будуть переноситись у кількох таблицях. У цей момент ви можете вирізати та наклеїти в клас ЕКСПОРТЕР та посипати їх туди, де потрібно. Тут грає інтерполяція назви класу з назвою таблиці та назви первинних індексів стовпців.

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

Коли використовувати DBI: Завжди.

Я не думаю, що це було ваше справжнє питання. Ваше справжнє запитання було: "Це схоже на величезний ПДФА, скажіть, будь ласка, що я не повинен цього робити?"

Ви цього не робите. Викладіть це правильно, і частина DBI стає достатньою, щоб мати змогу здебільшого автоматизувати її.


Будь-який шанс, що у вас є проект із відкритим кодом, яким ви могли б поділитися, а може бути навіть просто суть про github із прикладом кожного класу? Я думаю, що ідеї, які ви говорите, цікаві і, ймовірно, були б життєздатними для багатьох проектів, але було б трохи легше розпочати роботу з деякими прикладами.
msouth

0

Я не експерт Perl, але дуже багато його використовую. Є багато речей, яких я не знаю і не знаю, як зробити краще; деякі речі, я просто ще не здатний зрозуміти, незважаючи на документацію.

Я схильний починати з DBI, тому що я думаю: "О, це простий проект, мені не потрібно роздуття ORM, і я не хочу мати клопоту з модулями настройки та схеми". Але дуже швидко - майже кожного разу - я швидко починаю проклинати себе за це рішення. Коли я хочу почати проявляти творчість у своїх SQL-запитах (динамічні запити, а не лише порівняння заповнювачів), я намагаюся підтримувати розумність за допомогою DBI. SQL :: Анотація дуже допомагає, і, як правило, цього мені достатньо. Але моя наступна ментальна боротьба - це підтримка стільки SQL всередині мого коду. Мені дуже відволікає наявність рядків і ліній вбудованого SQL всередині потворних гередок. Можливо, мені потрібно використовувати якісний IDE.

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

(О, такі речі, як обмеження та тригери, є для мене величезним плюсом для DBIC.)


4
Ця відповідь не підходить до суті - звичайно, у вас є проблеми, коли ви використовуєте DBIx, але чому у вас виникають ці проблеми? Чи DBI не є гнучким, занадто щільно поєднаний з БД, не масштабується, чи що?
Джей Елстон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.