Rose::DB Perl ORM Быстрый страт. Создаём класс с коннектом к базе package My::DB; use warnings; use strict; use base qw(Rose::DB); __PACKAGE__->use_private_registry;

Презентация:



Advertisements
Похожие презентации
Catalyst and Rose::DB сборка. Rose::DB Описание работы с ORM смотри здесь.
Advertisements

Triggers для mysql. Что есть триггер? Триггер - это хранимая процедура особого типа, исполнение которой обусловлено наступлением определенного события.
Скриптовые языки на примере Perl. Языки программирования Скриптовые Программа (или ее бай-код) интерпретируется Зачастую более высокий уровень абстрагирования.
PL/SQL Курсоры. Курсор – специальный элемент, связанный с SQL-оператором SELECT. Объявление курсора происходит в секции объявления базового блока. Работа.
Вадим Думбравану руководитель проектов D7. ORM. Object-relational mapping Недостатки текущего похода в API На каждую сущность программируется свой GetList,
PL/SQL Взаимодействие с СУБД. Специальные типы Имя_таблицы.имя_столбца%TYPE Имя_таблицы%ROWTYPE; Имя_курсора%ROWTYPE;
Основы SQL Запросы к базе данных. Что такое база данных SQL? SQL (Structured Query Language - «Структурированный язык запросов») - универсальный компьютерный.
Занятие 4 def simple_iterator yield 2 # передача управления в блок итератора yield 7 yield 1 end simple_iterator {|i| puts i } 15. Итераторы изнутри.
Работа с БД в Zend Лекция 4 1.Модель 2.Классы Zend_Db_Table и Zend_Config 3.Запросы 4.Свойства и методы таблицы.
Лекция 3 Домены Ограничения на значения столбцов Создание, изменение и удаление таблиц Ключи и ссылочная целостность Защита таблиц.
Язык SQL Операторы манипулирования данными. Операции манипулирования данными DELETE операция удаления записей INSERT операция добавления или ввода новых.
СУБД Microsoft Access 2003 Элементы языка SQL. Язык SQL SQL (Structured Query Language) – структурированный язык запросов Язык SQL применяется во многих.
Базы данных. Введение Базы данных обеспечивают хранение информации. Доступ к базе данных осуществляется через специальную программу - систему управления.
Полиморфизм Полиморфизм (polymorphism) - последний из трех "китов", на которых держится объектно-ориентированное программирование Слово это можно перевести.
Содержание: 1. Управление данными. а) Извлечение данных команда SELECT; б) Полный список разделов. 2. Раздел SELECT. а) Синтаксис раздела SELECT; б) Ключевые.
САОД кафедра ОСУ 1 Основные абстрактные типы данных Схема процесса создания программ для решения прикладных задач ВУ.
Расширенные темы 1. SQL запросы Язык JPQL является абстракцией и «общим знаменателем» всех SQL диалектов. Очевидно, что конкретный диалект обладает бОльшими.
Введение в SQL (НЕ select) Затрагиваемые темы Роль языка SQL. Части SQL Роль языка SQL. Части SQL Администрирование БД: привилегии (DCL) Администрирование.
Система контроля прав доступа При помощи процедур и триггеров в MySQL.
Задачи поиска в структурах данных Поиск - нахождение какой-либо конкретной информации в большом объеме ранее собранных данных. Данные делятся на записи,
Транксрипт:

Rose::DB Perl ORM Быстрый страт

Создаём класс с коннектом к базе package My::DB; use warnings; use strict; use base qw(Rose::DB); __PACKAGE__->use_private_registry; __PACKAGE__->register_db( driver => 'mysql', type => 'main', database => 'databasename', host => '*****.***.***', username => 'user', password => '*****', connect_options => { AutoCommit => 1, RaiseError => 1, } ); 1;

При этом, учитываем, что в Rose::DB существует 2 параметра подключения к базе: type и domain. Т.о. вы можете варьировать между девелоперской и продакшн базой с одинаковыми структурами данных. Для это можно описать несколько вызовов метода register_db. В документации приводится даже пример: use My::DB; if($ENV{'MYCORP_PRODUCTION_SERVER'}) { My::DB->default_domain('production'); } else { My::DB->default_domain('development'); } Аналогично можно указывать и default_type для коннекта. Rose::DB::Cache – тут описывается вариант работы под mod_perl с/без использования Apache::DBI

Выливаем классы таблиц из базы #!/usr/bin/perl use strict; use warnings; use lib '/home/****/'; use My::DB; use Rose::DB::Object::Loader; my $loader = Rose::DB::Object::Loader->new( db => My::DB->new('main'), class_prefix => 'My::DB', with_foreign_keys => 1, with_relationships => 1 ); $loader->make_modules(module_dir=>'/home/****/',exclude_tables=>'Tmp'); exit; В данном случае передаётся доп параметр 'Tmp', чтобы исключить таблицы /Tmp/ (regexp) из итогового набора таблиц. При этом учитываем, что если у вас уже есть классы с описанием таблиц (со своими кастомными добавками), то они просто перезатрутся. Так же нужно учитывать, что если у вас есть поля с типом char(х), то это будет отмечено в классах, и когда вы будете забирать значения из из объекта строки для данной таблицы, значение будет дополнено пробелами до «x» символов.

Допустим у вас есть класс My::DB::MlPerson для получения объекта строки, с person_id достаточно дать: $p = My::DB::MlPerson->new(person_id=> )->load В конструктор передаётся первичный или уникальный ключ. «Прогрузка» данных произойдёт только в момент load. Может так случиться, что в базе нет строки с указанным значением ключа, и тогда вам вернётся warn. Поскольку иметь такие отбивки не интересно, да и не наглядно, рекомендуется использовать конструкцию с использованием параметра speculative: unless($p->load(speculative => 1)) { warn "not found"; } Получим и изменим значение поля password для нашей персоны: $p->password; $p->password('new_pass'); При установке значения в поле, проверяется его валидность на соответствие в описании класса. Сохраним изменённый объект строки в базе (update): $p->save; Создадим новую запись в базе: $p = My::DB::MlPerson->new(person_id=> ,password=>'pass'); $p->save; В данном контексте (создание записи), в конструктор совсем необязательно передавать авто-инкрементируемый ключ. Удаление записей абсолютно бесхитростно: $p = My::DB::MlPerson->new(person_id=> ); $p->delete; Как видно, load в данном случае не требуется.

Теперь перейдём к вопросам более сложных запросов в базу Когда вы выливали структуру таблицы в классы, то для каждой таблицы были созданы 2 модуля (на примере MlPerson): My::DB::MlPerson My::DB::MlPerson::Manager Идеология состоит в том, что если вам требуется создать некий дополнительный метод для таблицы, который бы возвращал обработанное значение имеющихся полей, то это удобно делать в My::DB::MlPerson. Примером является случай, когда у вас в поле хранится стерилизованная структура данных. Скажем, стерилизованная через Storable. Пример для такого поля stor_field: package My::DB::MlPerson; …. use Storable qw(thaw freeze); $Storable::interwork_56_64bit = 1; sub alias_to_stor_field{ my ($row_obj,$new_val) if ($new_val and ref $new_val) { # ставим новое значение $row_obj->stor_field(freeze($new_val)); return 1; } elsif ($row_obj->stor_field) { # десериализуем имеющееся return thaw($row_obj->stor_field); }; return undef; }

Но вот когда нам требуется обработать набор строк, вступает в свою силу My::DB::MlPerson::Manager. В самом модуле вы увидите: __PACKAGE__->make_manager_methods('НазваниеТаблицыВБазе'); Этот метод делает доступными следующие методы для класса My::DB::MlPerson::Manager: get_НазваниеТаблицыВБазе get_НазваниеТаблицыВБазе_iterator get_НазваниеТаблицыВБазе_count delete_НазваниеТаблицыВБазе update_НазваниеТаблицыВБазе Итак, для нашей таблицы ml_person, это будут: My::DB::MlPerson::Manager->get_ml_person возвращает ссылку на массив всех строк в таблице (что удобно в очень редких случаях, когда обращаешься по индексу, а не разыменовываешь массив. И, всё равно, не удобно.) My::DB::MlPerson::Manager->get_ml_person_iterator возвращает итератор для обхода всей таблицы. my $i = My::DB::MlPerson::Manager->get_ml_person_iterator; while( my $p = $i->next ) { print $p->name; $i->finish if(...); } Ахтунг на лицо. И всё было бы совсем плохо, НО …

НО эти методы умеют принимать аргументы для уточнения результатов поиска и возврата соответствующей ссылки на массив или итератор: my $i = My::DB::MlPerson::Manager->get_ml_person_iterator( query => [ name => {like => '%Hat'}, person_id => {ge => 7}, or => [ age => 15, age => {lt => 10}, ], sort_by => 'name', limit => 10, offset => 50 ); while( my $p = $i->next ) { print $p->name; $i->finish if(...); } Эти аргументы эквивалентны к вере: SELECT person_id, name, age,…,password FROM ml_person WHERE name LIKE '%Hat' AND person_id >= 7 AND (age = 15 OR age < 10.00) ORDER BY name LIMIT 10 OFFSET 50

Далее, My::DB::MlPerson::Manager->get_ml_person_count возвращает число записей в таблице. Так же умеет принимать аргументы, чтобы конкретизировать where в запросе на подсчёт. My::DB::MlPerson::Manager->delete_ml_person удаляет записи в соответствии с переданными аргументами. My::DB::MlPerson::Manager->update_ml_person обновляет записи в соответствии с переданными аргументами. Здесь стоит отметить, как передаётся аргумент set в верю: My::DB::MlPerson::Manager->update_ml_person( set => { age => 25, }, where => [ age => 24, person_id => { gt => 100 }, ] ); В любом из классов-описаний таблиц, вы можете выбрать стиль подключения, если перегрузите метод: Собственное подключение к базе: sub init_db { My::DB->new } Использовать уже созданное или новое: sub init_db { My::DB->new_or_cached }

Вы всегда можете получить объект My::DB $p = My::DB::MlPerson->new(...); $db = $p->db; И, организовать транзакционный механизм (если авто комет отключен): $p = My::DB::MlPerson->new(...); $db = $p->db; $db->begin_work; # Начало транзакции # Используем наш $db для каждого создаваемого объекта-строки $p1 = My::DB::MlPerson->new(name => 'Bike', db => $db); $p1->save; $p2 = My::DB::MlPerson->new(name => 'Sled', db => $db); $p2->save; $p3 = My::DB::MlPerson->new(name => 'Kite', db => $db); $p3->save; if(...) # Применяем изменения или откатываемся { $db->commit; } else { $db->rollback; }; А теперь вернёмся к My::DB и возможности регистрировать несколько коннектов к базам, играя параметрами type и domain.

Пусть у нас будут описаны в My::DB два коннекта для баз с одинаковыми структурами. Одна с domain=>production и вторая с domain=>archive. Назначение баз понятно. $production_db = My::DB->new('production'); $archive_db = My::DB->new('archive'); # Загрузим с продакшн базы данные по конкретной персоне $p = My::DB::MlPerson->new(person_id => 'John', db => $production_db); $p->load; # Скопируем данные по персоне в архив $p->db($archive_db); $p->save(insert => 1); # аргумент insert позволит либо записать строку, либо обновить значения # Удалим обозначенную персону из продакшн базы $p->db($production_db); $p->delete; Рассмотрим зависимости. Если у вас в базе они не были проставлены, то при генерации классов-таблиц, вы их то же не увидите. Прелесть в том, что можно прописать их ручками.

Отредактируем My::DB::MlPerson и добавим в метод setup вот такой аргумет: foreign_keys => [ crm => { class => 'My::DB::CrmBridge', key_columns => { person_id => 'f_person_id' }, relationship_type => 'one to one', }, ] Итак, что у нас здесь: создана связь между классами (не таблицами): My::DB::MlPerson и My::DB::CrmBridge. следующим образом: ml_person.person_id является внешним ключом для crm_bridge.f_person_id. Соответствие определено как один-к-одному. Обращение к отноению идёт по методу с именем crm. relationship_type может так же быть записан как rel_type (синоним) И, теперь мы можем сделать вот такую штуку: $p = My::DB::MlPerson->new(person_id=> )->load; $p->crm->any_field где, any_field - это любое поле из таблицы crm_bridge.

В общем-то, в данном случае можно было бы и опустить rel_type. Да и к тому же, можено вообще вынести этот аргумент в виде вызова метода (но очь важно, чтобы это было определено до вызова setup): package My::DB::MlPerson; __PACKAGE__->meta->foreign_keys( 'crm' => { class => 'My::DB::CrmBridge', key_columns => { person_id => 'f_person_id' } } ); __PACKAGE__->meta->setup(…………) А вот, интересный случай когда персона входит в несколько групп (в том же классе My::DB::MlPerson до setup): __PACKAGE__->meta->relationships ( group => { type => 'one to many', class => 'My::DB::MlGroup', column_map => { person_id => 'f_person_id' }, }, ); Здесь обозначено отношение один-ко-многим (одна персона из ml_persons может иметь несколько записей в ml_group). Как уже понятно, можно обозначит это отношение и в виде аргумента в методе setup, если вам так удобнее.

Что сразу не понравилось: $p = My::DB::MlPerson->new(person_id=> )->load; $bor = $p->group; ref $bor eq 'ARRAY'; другими словами – при обращении к отношению, вы получаете ссылку на массив или массив (в зависимости от контекста). На лицо явная избыточность. По документации не нашёл, как получить возможность обратиться к отношению с возможностью уточнения запроса (типа выбрать только конкретные группы для персоны) без выгребания всех записей из ml_group. И уж тем более, в продакшн всегда интереснее получить итератор, а тут И, более того, по дефолту (а это очевидно настраивается), повторное обращение: $p->group; уже не стало лезть в базу (а ведь со стороны кто-то мог поменять значение в строках таблицы), а просто отдало значение из Кеша (!). В общем, в данном вопросе, имхо, рулит DBIx::Class. Что можно назвать удобным, так это удаление групп, в которых состоит персона: $p = My::DB::MlPerson->new(person_id=> )->load; $p->group([ ]); $p->save; А вот пример тотально удаления всех связанных строк: $p->delete(cascade => 1); Для углубления вопроса по тому как составлять квери, рекомендую посмотреть Rose::DB::Object::Manager, где к сожалению не нашёл примера по вызову хранимок. Тему сисек считаю не раскрытой, но для быстрого старта – вполне достаточно.