BugzillaORM — различия между версиями

Материал из YourcmcWiki
Перейти к: навигация, поиск
(Багзилла)
м
Строка 1: Строка 1:
Здесь, наверное, будет статья, в которой я постараюсь выразить свои мысли по поводу ORM, обычного взгляда на вещи и необычного взгляда на вещи.
+
Bugzilla:
  
А пока что это сборище идей.
+
Есть сущности:
 +
* баг
 +
* классификация, продукт, компонент
 +
* пользователь
 +
* группа
  
== Типичный взгляд ==
+
У сущностей есть:
 +
* ID - первичный ключ
 +
* атрибуты
 +
* ссылка на контролирующую сущность (тип + ID) <br /> т.е. если другая сущность ссылается на эту, другим полем она должна ссылаться и на контролирующую
 +
* способ преобразования в строку
  
Пример: http://rutube.ru/tracks/793494.html - доклад с YAPC::Russia. Автор — Егор Шиповалов, название «Применение ORM в Perl».
+
Типы атрибутов:
 +
* Строка
 +
* Boolean
 +
* Decimal
 +
* Дата
 +
* Время
 +
* Дата+время
 +
* Single-Select &rarr; ссылка на сущность
 +
* Multi-Select &rarr; ссылка на несколько сущностей одного типа
 +
** Атрибут атрибута - в каком виде показывать список.
  
Первый же слайд ясно даёт понять проблемы, обычно решаемые авторами объектно-реляционных мапперов в Perl’е:
+
У всех атрибутов тоже есть ссылка на контролирующую сущность, означающая, что атрибут показывается, только если его сущность ссылается на его контролирующую сущность (или одну из них).
  
* Дублирование кода работы с БД.
+
Поля багов:
* Трудоёмкость адаптации кода под изменения схемы и различные СУБД.
+
<tab sep=bar class=simpletable head=left>
* Необходимость знания устройства и работы с БД всеми программистами команды.
+
short_desc         | строка
 +
classification          | single-select
 +
product                 | single-select, контролируется classification (или никем), у поля есть привязка к правам пользователя
 +
version                 | single-select, контролируется product
 +
component              | 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-select
 +
priority                | single-select, есть желание сделать decimal
 +
assigned_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 | boolean
 +
cclist_accessible | boolean
 +
estimated_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]».
+
Устаревшие поля / поля, которые ХЗ зачем нужны в таблице полей багов:
 
+
* assignee_accessible
Ещё одна почти бессмысленная для веб-приложений возможность: итераторы. Даже {{CPAN|DBR}} её, кстати, не лишён. То есть она осмысленная — в GUI-приложениях. Но не в веб-приложениях, где, во-первых, сам DBI никогда не держит открытых курсоров, во-вторых, это часто не даёт делать СУБД, а в-третьих, это совершенно не нужно, так как время исполнения запроса должно быть как можно меньше, а следующий запрос, вероятно, попадёт в другой поток — а может, и вообще в другой процесс.
+
* qacontact_accessible
 
+
* longdesc
Небольшой обзор существующих модулей ORM:
+
* commenter
 
+
* longdescs.isprivate
* {{CPAN|SPOPS}} — ужас кривой, тормозной, неиллюзорный, заброшенный в 2004 году.
+
* content
* {{CPAN|DBIx::Class}} — лидер области. Тяжеловесный, имеет кучу возможностей и вторую кучу — зависимостей. Например, использует {{CPAN|mro}} 'c3'. Частично совместим с Class::DBI. Но вы как хотите, а «БД-независимый» язык запросов, основанный на хешах — это ад. Что-то типа Vsem::DB::Query из [http://www.vsem.ru vsem.ru], кстати.
+
* bug_group
* {{CPAN|Class::DBI}} — говорят, легковесный. Наиболее старый — родился в 2001 году.
+
* flagtypes.name
* {{CPAN|ORM}} — сразу настораживает фраза «No SQL queries needed». '''Не бывает''' такого.
+
* requestees.login_name
* {{CPAN|DBR}} — весьма похож на Funq. Но явно попроще. Наиболее, кажется, «альтернативный».
+
* setters.login_name
* {{CPAN|Rose::DB::Object}} — ещё одна вариация на тему Class::DBI.
+
* {{CPAN|Class::AutoDB}}.
+
 
+
== Реальная проблема ==
+
 
+
Реальная проблема, ради решения которой задуман ORM — сложность, возникающая при попытке организовать сохранение в БД ''большого числа'' объектов '''разных типов''' со '''сложными отношениями''' друг между другом.
+
 
+
Даже [[lib:Bugzilla|Bugzilla]] имеет своё подобие ORM’а. И причина именно в этом — нужно управлять многими типами объектов. У бага много атрибутов, для каждого атрибута заведён отдельный тип «поле», а некоторые из этих типов одновременно являются самостоятельными объектами (продукты, компоненты). Отличительная черта багзильного ORM’а, по-моему, заключается в том, что он ориентирован на поля объектов и на универсальность доступа к значениям этих полей, но при этом не использует термина «связь» (один-к-одному, один-ко-многим и т. п.).
+
 
+
В целом этот ORM, конечно, кривоват и простоват. Но зато минимален и имеет одну очень правильную черту — он никогда не тратит силы на выполнение тех операций, о которых его ''пока ещё'' никто не просил. Создание экземпляра класса выглядит очень просто, в одну строчку — это просто bless хеша-строчки, полученной из БД. Никаких дополнительных манёвров при этом не происходит. А все методы, возвращающие те или иные данные, создают, извлекают или вычисляют эти данные только в момент первого вызова.
+
 
+
Для ORM’а «ленивая» идеология, с моей точки зрения, особенно важна, так как если на каждый чих будут извлекаться и создаваться кучи объектов — связанных и т. п. — даже тогда, когда это вообще не нужно, производительность, конечно, пострадает.
+
 
+
«Правильный» ORM должен делиться на две части:
+
 
+
# Обеспечивающий язык запросов и
+
# Схема объектного взаимодействия.
+
 
+
При этом язык запросов ''должен'' быть полным относительно возможностей нижележащего SQL. Аргументация простая: если полноты не будет, некоторый процент ситуаций потребует перехода на «ручное» выполнение собственных SQL-запросов, что обычно означает потерю всех преимуществ языка. Остаётся, конечно, вопрос процента таких ситуаций — если их очень мало, то и проблем, наверное, также будет очень мало.
+
 
+
Реально же многие (если не большинство) ORM-движков пытается скрестить ужа и ежа, то есть обе части, в одну.
+
 
+
== Багзилла ==
+
 
+
Есть объекты:
+
 
+
* Баг
+
* Продукт
+
* Группа
+
* ...
+
 
+
Для каждого объекта есть '''метакласс'''<ref>Название "метакласс" в данном контексте может вызвать негодование великих специалистов по ООП, но фиг с ним.</ref>. При старте приложения (или первой необходимости) создаётся объект метакласса - класс.
+
 
+
У объектов есть поля. Поля бывают разных типов. Для каждого типа полей тоже есть метакласс.
+
 
+
Например, описание поля связи с объектом (в скобках пример):
+
 
+
* Название поля (product)
+
* Объект, к которому относится поле (баг)
+
* Объект, с которым поле связывает (продукт)
+
* Поле, от которого зависит видимость поля
+
* Поле, от которого зависит набор значений поля (классификация)
+
** Значение по умолчанию, для каждого набора значений поля
+
* Активно?
+
* Выбирается при создании объекта?
+
* Редактируется?
+
 
+
Функции:
+
 
+
* Проверить значение при создании
+
* Проверить значение при обновлении
+
* Прочитать значение
+
* Записать значение
+
* + Любые другие
+
 
+
== FunMap ==
+
 
+
# Обеспечивающий язык запросов — [[Funq]].
+
# Схема объектного взаимодействия — ниже.
+
  
 +
Вычисляемые поля багов:
 +
<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>
  
 
[[Категория:Разработка]]
 
[[Категория:Разработка]]

Версия 16:48, 2 августа 2010

Bugzilla:

Есть сущности:

  • баг
  • классификация, продукт, компонент
  • пользователь
  • группа

У сущностей есть:

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

Типы атрибутов:

  • Строка
  • Boolean
  • Decimal
  • Дата
  • Время
  • Дата+время
  • Single-Select → ссылка на сущность
  • Multi-Select → ссылка на несколько сущностей одного типа
    • Атрибут атрибута - в каком виде показывать список.

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

Поля багов:

short_desc строка
classification single-select
product single-select, контролируется classification (или никем), у поля есть привязка к правам пользователя
version single-select, контролируется product
component 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-select
priority single-select, есть желание сделать decimal
assigned_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 boolean
cclist_accessible boolean
estimated_time время
remaining_time время
deadline дата
creation_ts дата+время
delta_ts дата+время
lastdiffed дата+время, скрыто в интерфейсе

Устаревшие поля / поля, которые ХЗ зачем нужны в таблице полей багов:

  • assignee_accessible
  • qacontact_accessible
  • longdesc
  • commenter
  • longdescs.isprivate
  • content
  • bug_group
  • flagtypes.name
  • requestees.login_name
  • setters.login_name

Вычисляемые поля багов:

work_time Сумма work_time от связанных longdescs
percentage_complete (Сумма work_time связанных багов)/(Сумма estimated_time связанных багов)
owner_idle_time Текущая дата минус MAX(дата последнего коммента от Assignee, дата последней активности от Assignee)
days_elapsed Текущая дата минус delta_ts
everconfirmed Менялся ли статус хоть раз на != UNCONFIRMED

Поля вложений:

bug_id single-select, показ в виде строки
submitter single-select, показ в виде select'а или строки, есть функция валидации (угадывания юзера по некорректному имени)
description single-select, показ в виде select'а или строки, есть функция валидации (угадывания юзера по некорректному имени)
filename строка
mimetype строка
ispatch boolean
isobsolete boolean
isprivate boolean
isurl boolean
thedata потенциально строка, а вообще-то обычно NULL, т.к. данные хранятся в локальных файлах