Изменения

BugzillaORM

4624 байта убрано, 13:48, 2 августа 2010
м
Нет описания правки
Здесь, наверное, будет статья, в которой я постараюсь выразить свои мысли по поводу ORM, обычного взгляда на вещи и необычного взгляда на вещи.Bugzilla:
А пока что это сборище идей.Есть сущности:* баг* классификация, продукт, компонент* пользователь* группа
== Типичный взгляд ==У сущностей есть:* ID - первичный ключ* атрибуты* ссылка на контролирующую сущность (тип + ID) <br /> т.е. если другая сущность ссылается на эту, другим полем она должна ссылаться и на контролирующую* способ преобразования в строку
ПримерТипы атрибутов: http://rutube.ru/tracks/793494.html * Строка* Boolean* Decimal* Дата* Время* Дата+время* Single-Select &rarr; ссылка на сущность* Multi-Select &rarr; ссылка на несколько сущностей одного типа** Атрибут атрибута - доклад с YAPC::Russia. Автор — Егор Шиповалов, название «Применение ORM в Perl»каком виде показывать список.
Первый же слайд ясно даёт понять проблемыУ всех атрибутов тоже есть ссылка на контролирующую сущность, обычно решаемые авторами объектно-реляционных мапперов в Perl’е:означающая, что атрибут показывается, только если его сущность ссылается на его контролирующую сущность (или одну из них).
* Дублирование кода работы с БД.Поля багов:* Трудоёмкость адаптации кода под изменения схемы и различные СУБД.<tab sep=bar class=simpletable head=left>* Необходимость знания устройства и работы с БД всеми программистами командыshort_desc | строкаclassification | single-selectproduct | single-select, контролируется classification (или никем), у поля есть привязка к правам пользователяversion | single-select, контролируется productcomponent | single-select, контролируется rep_platform | single-select, специальная функция "угадывания" дефолтного значенияbug_file_loc | строкаop_sys | single-select, специальная функция "угадывания" дефолтного значенияbug_status | single-select, есть функция валидации (Bug Status Workflow)resolution | single-select, у атрибута есть контролирующая сущность (показывается только при bug_status.closed=1)status_whiteboard | строкаkeywords | multi-select, показ в виде спискаbug_severity | single-selectpriority | single-select, есть желание сделать decimalassigned_to | single-select, показ в виде select'а или строки, есть функция валидации (угадывания юзера по некорректному имени)reporter | single-select, показ в виде select'а или строки, есть функция валидации (угадывания юзера по некорректному имени)qa_contact | single-select, показ в виде select'а или строки, есть функция валидации (угадывания юзера по некорректному имени)votes | decimal(,0)cc | multi-select, показ в виде combo-box'а, есть функция валидации (угадывания юзера по некорректному имени)dependson | multi-select, показ в виде спискаblocked | multi-select, показ в виде спискаtarget_milestone | строкаsee_also | multi-select, показ в виде списка (багов)alias | строкаreporter_accessible | booleancclist_accessible | booleanestimated_time | времяremaining_time | времяdeadline | датаcreation_ts | дата+времяdelta_ts | дата+времяlastdiffed | дата+время, скрыто в интерфейсе</tab>
Так вот, все эти причины — «[[lurkmore:4.2|4.2 (чёрта с два)]]». Бесплатная подсказка, тривиальный путь решения всех трёх проблем: код работы с БД для каждого «компонента» программы выносится в отдельный класс и разбивается на отдельные процедуры. При грамотном разбиении кода работы с БД на отдельные процедуры он получается достаточно компактным, а если не компактным, то по меньшей мере простым и очевидным. Весь код работы с БД таким образом концентрируется в одном месте, и переписывать под различные СУБД его становится гораздо проще. И уж конечно, имея код работы с БД в отдельном классе, неспециалисты по БД могут и не заниматься работой с ней. Между прочим, этот подход использует [http:Устаревшие поля //www.skype.com/ Skype]поля, за тем исключением, что функционал которые ХЗ зачем нужны в процедуры они оборачивали не на стороне клиента БД, а внутри самой СУБД ([http://www.postgresql.org/ PostgreSQL]) — об этом Иван Золотухин рассказывал в статье «[http://postgresmen.ru/articles/view/25 Масштабирование PostgreSQL: Готовые решения от Skype]». Ещё одна почти бессмысленная для веб-приложений возможность: итераторы. Даже {{CPAN|DBR}} её, кстати, не лишён. То есть она осмысленная — в GUI-приложениях. Но не в веб-приложениях, где, во-первых, сам DBI никогда не держит открытых курсоров, во-вторых, это часто не даёт делать СУБД, а в-третьих, это совершенно не нужно, так как время исполнения запроса должно быть как можно меньше, а следующий запрос, вероятно, попадёт в другой поток — а может, и вообще в другой процесс. Небольшой обзор существующих модулей ORMтаблице полей багов:* assignee_accessible* {{CPAN|SPOPS}} — ужас кривой, тормозной, неиллюзорный, заброшенный в 2004 году.qacontact_accessible* {{CPAN|DBIx::Class}} — лидер области. Тяжеловесный, имеет кучу возможностей и вторую кучу — зависимостей. Например, использует {{CPAN|mro}} 'c3'. Частично совместим с Class::DBI. Но вы как хотите, а «БД-независимый» язык запросов, основанный на хешах — это ад. Что-то типа Vsem::DB::Query из [http://www.vsem.ru vsem.ru], кстати.longdesc* {{CPAN|Class::DBI}} — говорят, легковесный. Наиболее старый — родился в 2001 году.commenter* {{CPAN|ORM}} — сразу настораживает фраза «No SQL queries needed». '''Не бывает''' такогоlongdescs.isprivate* {{CPAN|DBR}} — весьма похож на Funq. Но явно попроще. Наиболее, кажется, «альтернативный».content* {{CPAN|Rose::DB::Object}} — ещё одна вариация на тему Class::DBI.bug_group* {{CPAN|Class::AutoDB}}flagtypes== Реальная проблема == Реальная проблема, ради решения которой задуман ORM — сложность, возникающая при попытке организовать сохранение в БД ''большого числа'' объектов '''разных типов''' со '''сложными отношениями''' друг между другом. Даже [[lib:Bugzilla|Bugzilla]] имеет своё подобие ORM’а. И причина именно в этом — нужно управлять многими типами объектов. У бага много атрибутов, для каждого атрибута заведён отдельный тип «поле», а некоторые из этих типов одновременно являются самостоятельными объектами (продукты, компоненты). Отличительная черта багзильного ORM’а, по-моему, заключается в том, что он ориентирован на поля объектов и на универсальность доступа к значениям этих полей, но при этом не использует термина «связь» (один-к-одному, один-ко-многим и т. п.). В целом этот ORM, конечно, кривоват и простоват. Но зато минимален и имеет одну очень правильную черту — он никогда не тратит силы на выполнение тех операций, о которых его ''пока ещё'' никто не просил. Создание экземпляра класса выглядит очень просто, в одну строчку — это просто bless хеша-строчки, полученной из БД. Никаких дополнительных манёвров при этом не происходит. А все методы, возвращающие те или иные данные, создают, извлекают или вычисляют эти данные только в момент первого вызова. Для ORM’а «ленивая» идеология, с моей точки зрения, особенно важна, так как если на каждый чих будут извлекаться и создаваться кучи объектов — связанных и т. п. — даже тогда, когда это вообще не нужно, производительность, конечно, пострадает. «Правильный» ORM должен делиться на две части: # Обеспечивающий язык запросов и# Схема объектного взаимодействия. При этом язык запросов ''должен'' быть полным относительно возможностей нижележащего SQL. Аргументация простая: если полноты не будет, некоторый процент ситуаций потребует перехода на «ручное» выполнение собственных SQL-запросов, что обычно означает потерю всех преимуществ языка. Остаётся, конечно, вопрос процента таких ситуаций — если их очень мало, то и проблем, наверное, также будет очень мало. Реально же многие (если не большинство) ORM-движков пытается скрестить ужа и ежа, то есть обе части, в одну. == Багзилла == Есть объекты: * Баг* Продукт* Группаname* requestees... Для каждого объекта есть '''метакласс'''<ref>Название "метакласс" в данном контексте может вызвать негодование великих специалистов по ООП, но фиг с ним.</ref>. При старте приложения (или первой необходимости) создаётся объект метакласса - класс. У объектов есть поля. Поля бывают разных типов. Для каждого типа полей тоже есть метакласс. Например, описание поля связи с объектом (в скобках пример):login_name* Название поля (product)* Объект, к которому относится поле (баг)* Объект, с которым поле связывает (продукт)* Поле, от которого зависит видимость поля* Поле, от которого зависит набор значений поля (классификация)** Значение по умолчанию, для каждого набора значений поля* Активно?* Выбирается при создании объекта?* Редактируется? Функции: * Проверить значение при создании* Проверить значение при обновлении* Прочитать значение* Записать значение* + Любые другие == FunMap == # Обеспечивающий язык запросов — [[Funq]].# Схема объектного взаимодействия — нижеsetters.login_name
Вычисляемые поля багов:
<tab sep=bar class=simpletable head=left>
work_time | Сумма work_time от связанных longdescs
percentage_complete | (Сумма work_time связанных багов)/(Сумма estimated_time связанных багов)
owner_idle_time | Текущая дата минус MAX(дата последнего коммента от Assignee, дата последней активности от Assignee)
days_elapsed | Текущая дата минус delta_ts
everconfirmed | Менялся ли статус хоть раз на != UNCONFIRMED
</tab>
Поля вложений:
<tab sep=bar class=simpletable head=top>
bug_id | single-select, показ в виде строки
submitter | single-select, показ в виде select'а или строки, есть функция валидации (угадывания юзера по некорректному имени)
description | single-select, показ в виде select'а или строки, есть функция валидации (угадывания юзера по некорректному имени)
filename | строка
mimetype | строка
ispatch | boolean
isobsolete | boolean
isprivate | boolean
isurl | boolean
thedata | потенциально строка, а вообще-то обычно NULL, т.к. данные хранятся в локальных файлах
</tab>
[[Категория:Разработка]]