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

Andrii Kostenko andrey на kostenko.name
Ср Окт 26 10:09:04 PDT 2011


Вы готовите его неправильно

1. Нужно подменять storage, а не модель.
2. Если правильно выбирать has_one/belongs_to, то join_type => 'left' нужен в исключительных случаях
3. К сожалению, да. DBIx::Class работает хорошо только с хорошей структурой БД. Когда все просто и не нужно делать SQL-извращений для получения результата.


On 26.10.2011, at 20:26, Ivan Petrov wrote:

> сделали мы тут цельный проект на 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 mailing list
> moscow-pm на pm.org | http://moscow.pm.org



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