[Moscow.pm] Размышления на тему ORM и вообще работы с БД

Ivan Petrov i.petro.77.00 на gmail.com
Ср Окт 26 09:26:47 PDT 2011


сделали мы тут цельный проект на DBIx::Class. С точки зрения
попробовать последний. До этого всегда использовали DBI, который
использовала модель в системе MVC.

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

В первую очередь зачем как бы нужен был этот DBIC.
 - конструирование SQL-запросов
 - уход от смешивания кода Perl с кодом SQL
 - выборка объектов, а не хешей из БД, которые можно расширять своими
   методами
 - упрощение кода моделей с точки зрения что в простых случаях вообще
   не делать выделенную модель

Ничего не забыл?

По результатам проекта (проект был довольно большой и дальше еще будет
долго) я понял следующее:

1. даже в простых случаях DBIC на роль модели не годится. То есть в
любом случае нужно делать выделенную модель, пусть даже там один
resultset('Name')->find. Ибо тесты. При их написании мы применяем
способ "подменить модель на стадии тестирования".

2. DBIC не предоставляет хорошей абстракции над БД. Все равно
JOIN'ы, SELECT'ы торчат местами (например join_type => 'left' в has'ах
итп). То есть оперировать хранилищем как некоей абстракцией в реальном
случае не получается.

3. Конструктор SQL-запросов крайне страшен. Работали мы с постоянно
включенной отладкой (выводом всех делающихся запросов в лог). С одной
стороны есть множество вещей которые на DBIC сделать крайне непросто
(например многие аггрегаторные вещи), с другой стороны когда
начинается работа с более чем двумя таблицами он зачастую мутит такие
страшные вложенные SQL-запросы что волосы дыбом становятся, а с
третьей стороны использование фич конкретной БД становится затруднено.

В итоге пришли к тому, что в сложных случаях приходится делать в БД
view'ы только из за того что от DBIC добиться генерации нормального
запроса очень и очень сложно или невозможно. Набор опций при выборке
может быть больше самого SQL-запроса, если его просто написать.

Ну вот и в свете того, что код работы с DBIC все равно располагается в
моделях, конструировать эффективные SQL-запросы он все равно не умеет,
то из плюсов его использования остаются только:
 - выборка в объекты и итераторы по ним
 - уход от смешивания Perl и SQL

Соответственно размышляя над всем этим мы пришли к тому, что 

1. построить качественный автогенератор SQL запросов на все случаи
жизни (или по кр. мере на большинство) невозможно, поэтому его не
стоит и пытаться сделать

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

то есть надо

1. написать свой итератор, работающий поверх массива или
хеша - выборки из БД.
2. написать базовую обертку над одной строкой выборки (доступ в виде
методов)
3. Используя 1 и 2 написать над DBI класс, который выборки блессает в
итераторы и объекты
4. решить вопрос с выносом SQL-кода из перловых модулей. То есть
обертка 3 должна уметь работать с SQL, располагающимися в файлах.


Вынос SQL в файлы очень плохо соотносится с дефолтными плейсхолдерами
DBI. поскольку они позиционные.

Вывод:
5. надо реализовывать именованные плейсхолдеры

ну и попутно 
5.1 реализовать плейсхолдерные (темплейтные) выражения для типовых
вещей, как-то
 INSERT ... VALUES (?,?,?),(?,?,?),(?,?,?),...

или
 SELECT ... WHERE id IN (?,?,?)

то есть подстановки массивов.

Соответственно исходя из этого родился набор модулей, который в
ближайшее время, если кому-то будет интересно, мы можем выложить на
CPAN (там еще работы на неск. дней осталось), который все это делает:

1. по дефолту блесает строки-выборки в объект, у которого три
предопределенных метода: new, is_changed, iterator. Плюс методы
совпадающие с именами выбранных столбиков.

2. блесает наборы-выборки в объект позволяющий делать проходы по
выборкам итп (совместимо с DBIC'шными ->all, while(...->next) итп

3. в п. 1 и 2 позволяет указывать свои классы для итераторов и выборок

4. позволяет вместо SQL использовать имена файлов SQL в предзаданной
директории (по аналогии с темлейтами любого вебпроекта)

5. реализует следующий набор подстановок в SQL

    ?{путь} - просто подставляет переменную
    ?@{путь} - подставляет массив переменных (через запятую)
    ?%{путь}{поле,поле,...} - подставляет массив переменных (через
          запятую) из массива хешей
    
    ну и на вс случай сделали
    ?sub{перловый код}


    А так же набор условных подстановок вроде

    ?if[de]?{путь}{ блок который вставится }{else-блок}

    условия - if - истина
              ifd - определен
              ife - имеется

   Блоки можно вкладывать друг в друга.

   пути - по сути путь к переменной во входном хеше, то есть

   $dbh->do($sql, values => { ids => [1, 3, 5] }, abc => 'def');

   путями являются
   'values'
   'abc'
   'values.ids'

   и так далее.

   поддерживаются в путях и объекты

   path.to:method  - будет вызван метод объекта, а значение им
   возвращенное будет вставлено в SQL.


В итоге что получили:

1. SQL вынесен в отдельный каталог (отдельные каталоги) примерно так
же как вынесены темплейты в любом вебпроекте
2. типичные SQL-вещи делаются просто. Сложные тоже можно делать.
3. Поскольку итератор по именам методов постарались сделать
совместимым с DBIC то замена одного на другое не вызывает больших
трудностей.

ну и возьмем пример:

скажем приходит от пользователя форма

$filters = { region => 'Москва', street => '' };

То есть он фильтр по региону заполнил, а фильтр по улице нет

запрос может выглядеть так

SELECT
    *
FROM
    tbl1
LEFT JOIN tbl2 ...

WHERE
    something = ..

    ?if{filters.region}{ AND region = ?{filters.region} }
    ?if{filters.street}{ AND street = ?{filters.street} }

и так далее.

Получается SQL перестраивается в зависимости от того что там вводит
пользователь. 

Осталось нерешенными некоторые типовые вопросы, но мы их планируем
порешать в ближайшее время (например подставновки частей в
like-секции)

соответственно вопросы:

1. кому-нибудь интересна подобная система, стоит класть на CPAN?
2. может на эту тему есть что-то готовое да мы велик очередной ваяем?
тогда ткните указкой:)


Подробная информация о списке рассылки Moscow-pm