Лекция
Привет, сегодня поговорим про orm, обещаю рассказать все что знаю. Для того чтобы лучше понимать что такое orm, object-relational mapping, объектно-реляционное отображение, active record, data mapper, dao, activerecord, datamapper , настоятельно рекомендую прочитать все из категории Базы данных, знаний и хранилища данных. Big data, СУБД и SQL и noSQL.
ORM (англ. object-relational mapping, рус. объектно-реляционное отображение ) — технология программирования, которая связывает базы данных с концепциямиобъектно-ориентированных языков программирования, создавая «виртуальную объектную базу данных». Существуют как проприетарные, так и свободные реализации этой технологии.
В объектно-ориентированном программировании объекты в программе представляют объекты из реального мира. В качестве примера можно рассмотреть адресную книгу, которая содержит список людей с нулем или более телефонов и нулем или более адресов. В терминах объектно-ориентированного программирования они будут представляться объектами класса «Человек», которые будут содержать следующий список полей: имя, список (или массив) телефонов и список адресов.
Суть задачи состоит в преобразовании таких объектов в форму, в которой они могут быть сохранены в файлах или базах данных, и которые легко могут быть извлечены в последующем, с сохранением свойств объектов и отношений между ними. Эти объекты называют «хранимыми» (англ. persistent). Исторически существует несколько подходов к решению этой задачи.
Решение проблемы хранения данных существует — это реляционные системы управления базами данных. Использование реляционной базы данных для хранения объектно-ориентированных данных приводит к семантическому разрыву, заставляя программистов писать программное обеспечение, которое должно уметь как обрабатывать данные в объектно-ориентированном виде, так и уметь сохранить эти данные в реляционной форме. Эта постоянная необходимость в преобразовании между двумя разными формами данных не только сильно снижает производительность, но и создает трудности для программистов, так как обе формы данных накладывают ограничения друг на друга.
Реляционные базы данных используют набор таблиц, представляющих простые данные. Дополнительная или связанная информация хранится в других таблицах. Часто для хранения одного объекта в реляционной базе данных используется несколько таблиц; это, в свою очередь, требует применения операции JOIN для получения всей информации, относящейся к объекту, для ее обработки. Например, в рассмотренном варианте с записной книгой, для хранения данных, скорее всего, будут использоваться как минимум две таблицы: люди и адреса, и, возможно, даже таблица с телефонными номерами.
Так как системы управления реляционными базами данных обычно не реализуют реляционного представления физического уровня связей, выполнение нескольких последовательных запросов (относящихся к одной «объектно-ориентированной» структуре данных) может быть слишком затратно. В частности, один запрос вида «найти такого-то пользователя и все его телефоны и все его адреса и вернуть их в таком формате», скорее всего, будет выполнен быстрее серии запросов вида «Найти пользователя. Найти его адреса. Найти его телефоны». Это происходит благодаря работе оптимизатора и затратам на синтаксический анализ запроса.
Некоторые реализации ORM автоматически синхронизируют загруженные в память объекты с базой данных. Для того чтобы это было возможным, после создания объект-в-SQL-преобразующего SQL-запроса (класса, реализующего связь с DB) полученные данные копируются в поля объекта, как во всех других реализациях ORM. После этого объект должен следить за изменениями этих значений и записывать их в базу данных.
Системы управления реляционными базами данных показывают хорошую производительность на глобальных запросах, которые затрагивают большой участок базы данных, но объектно-ориентированный доступ более эффективен при работе с малыми объемами данных, так как это позволяет сократить семантический провал между объектной и реляционной формами данных.
При одновременном существовании этих двух разных миров увеличивается сложность объектного кода для работы с реляционными базами данных, и он становится более подвержен ошибкам. Разработчики программного обеспечения, основывающегося на базах данных, искали более легкий способ достижения постоянства их объектов.
использование ORM решает проблему так называемой парадигмы «несоответствия», которая гласит о том, что объектные и реляционные модели не очень хорошо работают вместе. Реляционные базы представляют данные в табличном формате, в то время как объектно-ориентированные языки представляют их как связанный граф объектов. Основные проблемы и несоответствия возникают во время сохранения этого графа объектов в реляционную базу или его загрузки:
Разработано множество пакетов, устраняющих необходимость в преобразовании объектов для хранения в реляционных базах данных.
Некоторые пакеты решают эту проблему, предоставляя библиотеки классов, способных выполнять такие преобразования автоматически. Имея список таблиц в базе данных и объектов в программе, они автоматически преобразуют запросы из одного вида в другой. В результате запроса объекта «человек» (из примера с адресной книгой) необходимый SQL-запрос будет сформирован и выполнен, а результаты «волшебным» образом преобразованы в объекты «номер телефона» внутри программы.
С точки зрения программиста система должна выглядеть как постоянное хранилище объектов. Он может просто создавать объекты и работать с ними как обычно, а они автоматически будут сохраняться в реляционной базе данных.
На практике все не так просто и очевидно. Все системы ORM обычно проявляют себя в том или ином виде, уменьшая в некотором роде возможность игнорирования базы данных. Более того, слой транзакций может быть медленным и неэффективным (особенно в терминах сгенерированного SQL). Все это может привести к тому, что программы будут работать медленнее и использовать больше памяти, чем программы, написанные «вручную».
Но ORM избавляет программиста от написания большого количества кода, часто однообразного и подверженного ошибкам, тем самым значительно повышая скорость разработки. Об этом говорит сайт https://intellect.icu . Кроме того, большинство современных реализаций ORM позволяют программисту при необходимости самому жестко задать код SQL-запросов, который будет использоваться при тех или иных действиях (сохранение в базу данных, загрузка, поиск и т. д.) с постоянным объектом.
Ключевой особенностью ORM является отображение, которое используется для привязки объекта к его данным в БД. ORM как бы создает «виртуальную» схему базы данных в памяти и позволяет манипулировать данными уже на уровне объектов. Отображение показывает как объект и его свойства связанны с одной или несколькими таблицами и их полями в базе данных. ORM использует информацию этого отображения для управления процессом преобразования данных между базой и формами объектов, а также для создания SQL-запросов для вставки, обновления и удаления данных в ответ на изменения, которые приложение вносит в эти объекты.
Использование ORM в проекте избавляет разработчика от необходимости работы с SQL и написания большого количества кода, часто однообразного и подверженного ошибкам. Весь генерируемый ORM код предположительно хорошо проверен и оптимизирован, поэтому не нужно в целом задумывается о его тестировании. Это несомненно является плюсом, но в тоже время не стоит забывать и о минусах. Основной из них — это потеря производительности. Это происходит потому, что большинство ORM предназначены для обработки широкого спектра сценариев использования данных, гораздо большего, чем любое отдельное приложение когда-либо сможет использовать. Вопрос о целесообразности использования ORM по большому счету затрагивается только в больших проектах, которые сталкиваются с высокой нагрузкой, здесь приходится выбирать что более приоритетно — удобство или производительность? Конечно, работа с БД посредством грамотно написанного SQL-кода будет намного эффективнее, но не стоит забывать и о таком параметре, как время — то, что с легкостью пишется с использованием ORM за неделю, можно реализовывать ни один месяц собственными усилиями. Кроме того, большинство современных ORM позволяют программисту при необходимости самому задавать код SQL-запросов. Без сомнений, для небольших проектов использование ORM будет куда более оправдано, чем разработка собственных библиотек для работы с БД.
Касательно альтернатив технологии ORM, то среди них выделяются:
В случае CoRM отсутствует ограничение на возможность хранить состояние разных объектов одного класса в разных коллекциях и ограничение на одновременное хранение в коллекции объектов, которые принадлежат разным классам.
От обычного ORM-подхода реляционное отображение коллекций отличает то, что коллекция напрямую не привязана к классу и, в теории, может включать в себя любой объект, в случае соблюдения некоторых минимальных требований к данному объекту, например, требование наличия способности к сериализации). Однако к этим требованиям не относятся ограничения на структуру объекта либо использование особых типов данных.
ORM — это инструмент решения проблемы семантического провала между реляционной и объектной моделями данных. Имеющий, однако, определенные проблемы, которых должны быть лишены его альтернативы, позволяющие вывести объектную сущность приложения из ограничений, накладываемых реляционным хранилищем.
Эти 2 шаблона проектирования описаны в книге Мартина Фаулера «Шаблоны корпоративных приложений» и представляют собой способы работы с сохранением данных в объектно-ориентированном программировании.
class Foo { protected $db; public $id; public $bar; public function __construct(PDO $db) { $this->db = $db; } public function do_something() { $this->bar .= uniqid(); } public function save() { if ($this->id) { $sql = "UPDATE foo SET bar = :bar WHERE id = :id"; $statement = $this->db->prepare($sql); $statement->bindParam("bar", $this->bar); $statement->bindParam("id", $this->id); $statement->execute(); } else { $sql = "INSERT INTO foo (bar) VALUES (:bar)"; $statement = $this->db->prepare($sql); $statement->bindParam("bar", $this->bar); $statement->execute(); $this->id = $this->db->lastInsertId(); } } } //Insert $foo = new Foo($db); $foo->bar = 'baz'; $foo->save();
В этом упрощенном примере, дескриптор базы данных вводится в конструкторе Foo (Использование инъекции зависимостей здесь позволяет тестировать объект без использования реальной базы данных), и Foo использует его, чтобы сохранять свои данные. Do_something — просто метод-заглушка, заменяющий бизнес логику.
class Foo { public $id; public $bar; public function do_something() { $this->bar .= uniqid(); } } class FooMapper { protected $db; public function __construct(PDO $db) { $this->db = $db; } public function saveFoo(Foo &$foo) { if ($foo->id) { $sql = "UPDATE foo SET bar = :bar WHERE id = :id"; $statement = $this->db->prepare($sql); $statement->bindParam("bar", $foo->bar); $statement->bindParam("id", $foo->id); $statement->execute(); } else { $sql = "INSERT INTO foo (bar) VALUES (:bar)"; $statement = $this->db->prepare($sql); $statement->bindParam("bar", $foo->bar); $statement->execute(); $foo->id = $this->db->lastInsertId(); } } } //Insert $foo = new Foo(); $foo->bar = 'baz'; $mapper = new FooMapper($db); $mapper->saveFoo($foo);
В данном случае, класс Foo намного проще и должен беспокоиться только о своей бизнес-логике. Он не только не должен сохранять собственные данные, он даже не знает и не заботится о том, все ли его данные были сохранены.
При использовании шаблона проектирования Data Mapper, вызывающий код должен выбрать Mapper и бизнес-объект и связать их вместе. Если это код вызова в контроллере, то в конечном счете ваша модель «утекает» в контроллер, что может вызвать большие проблемы при поддержке и юнит-тестировании. Эта проблема может быть решена путем введения объекта-сервиса. Сервис является воротами между контроллером и моделью и связывает доменный объект с Mapper-ом по мере необходимости.
Следует помнить, что M в MVC, представляет собой слой абстракции модели, а не объект модели. Так может быть несколько типов объектов в одной модели (в приведенном выше примере, у вас может быть объект сервиса, доменный объект и объект Mapper-а, выступающие в роли единой модели). С другой стороны, если вы используете модели Active Record, ваша модель может быть представлена лишь одним объектом.
Объекты Active Record исторически были очень популярны из-за того, что они проще, легче в понимании и быстрее в написании, поэтому многие фреймворки и ORM используют Active Record по умолчанию, напрмиер (Laravel).
Если вы уверены, что вам никогда не понадобиться менять слой сохранения данных (если вы имеете дело с объектом, который представляет из себя INI-файл, например), или вы имеете дело с очень простыми объектами, в которых не так много бизнес-логики, или просто предпочитаете держать все в небольшом количестве классов, тогда шаблон Active Record это то, что вам нужно.
Использование Data Mapper-а хотя и ведет к более чистому, простому в тестировании и поддержке коду, и обеспечивает большую гибкость, — цена этому, — повышение сложности (напрмиер Doctrine v>2 в Symfony).
Doctrine версии 1.* следует паттерну Active Record для работы с данными. Для примера, если программист хочет создать пользователя в базе данных, он может больше не использовать SQL, а написать следующий PHP код:
$user = new User(); $user->name = "john"; $user->password = "doe"; $user->save(); echo "The user with id $user->id has been saved.";
Doctrine версии 2.* следует паттерну Data mapper . Для создания пользователя может использоваться следующий кодː
$user = new User(); $user->setName("john"); $user->setPassword("doe"); $entityManager->persist($user); $entityManager->flush(); echo "The user with id " . $user->getId() . "has been saved.";
В популярных фреймворках для работы с базой данных, как правило, используется ORM, которая представляет собой вспомогательную прослойку между приложением и базой данных.
В зависимости от фреймворка, ORM может реализовывать паттерн Active Record (например, framework kohana, Yii) или Data Mapper (например, doctrine в symfony). ORM фреймворка также может реализовывать другой паттерн или гибрид.
ORM — Object Relational Mapping. Говоря самым простым языком, ряды из таблиц в базе данных, будут представлены в виде объектов, проперти которых соответствуют именам полей из таблиц, а значения пропертей объекта - значениям из базы данных. Одна строка в базе данных - один объект.
Итак, идем от более простого к более сложному: DAO -> Active Record -> Data Mapper.
Используется в библиотеке DBSimple.
DAO (Data Access Object) - объект, который предоставляет абстрактный интерфейс к базе данных. Главной идеей DAO является сделать возможным определенные операции с данными не вдаваюсь в детали реализации базы данных.
При использовании DAO - функции для работы c конкретной таблицей хранятся в файле модели. Модель (таблица) наследует абстрактный класс, реализующий DAO.
При получение ряда в DAO - в результирующем объекте или массиве будут содержаться все поля из базы данных. Пример:
В переменной $user будет объект либо массив в зависимости от реализации, содержащий все поля из таблицы "user". Класс TableUser будет содержать все методы, которые работают с таблицей "user". Дополнительный класс репозитория тут не нужен. Почему? Если для получения данных создать класс репозиторий, то в модели (таблице) останется буквально единственный метод получения имени таблицы.
Для обновления данных в DAO - используются отдельные методы, реализующие прямые sql-запросы, в отличие от Active Record где меняется состояние модели и вызывается save() для персиста состояния в базу данных.
Используется в Kohana, Yii.
Это шаблон проектирования или один из слоев приложения, который несет ответственность за представление бизнес-логики и данных. Active Record позволяет создавать и использовать более просто те объекты, который требуют постоянного хранения в базе данных. Если говорить в отношении MVC, то Active Record реализует первую букву М - то есть модель.
Очень простой пример использования модели, реализующей паттерн Active Record в php:
Этот код вызовет генерацию такого sql-запроса:
Как правило, внутри модели, которая реализует Active Record, прописаны property. В данном случае в модели User должны быть представлены как минимум property name, email. Содержимое класса User:
Проперти, как правило, делают публичными, как в примере выше. Но, проперти модели могут быть и приватными, тогда отдельно создают сеттеры и геттеры под каждый проперти.
Как правило, Active Record Модель - это маппинг полей модели на поля в базе данных. В Active record сама модель отвечает за сохранение данных в базу данных. А это означает, что нарушется первый принцип из SOLID - принцип единственности ответственности. Класс отвечает не только за представление данных, но и за сохранение.
Используется в Hibernate в Java и в Doctrine2 в php, так в CycleOrm.
Data Mapper - это слой доступа к данным, который предоставляет двунаправленный маппинг данных между постоянным хранилищем данных (обычно, это sql база данных) и хранением данных в памяти (например, на время выполнениния php скрипта).
В отличие от Active Record, в Data Mapper появляется еще один слой или тип сущности такой как entityManager. Именно этот слой будет отвечать за перенос состояния модели в базу данных и обратно.
Сохранение объекта
Надеюсь, эта статья про orm, была вам полезна, счастья и удачи в ваших начинаниях! Надеюсь, что теперь ты понял что такое orm, object-relational mapping, объектно-реляционное отображение, active record, data mapper, dao, activerecord, datamapper и для чего все это нужно, а если не понял, или есть замечания, то не стесняйся, пиши или спрашивай в комментариях, с удовольствием отвечу. Для того чтобы глубже понять настоятельно рекомендую изучить всю информацию из категории Базы данных, знаний и хранилища данных. Big data, СУБД и SQL и noSQL
Комментарии
Оставить комментарий
Базы данных, знаний и хранилища данных. Big data, СУБД и SQL и noSQL
Термины: Базы данных, знаний и хранилища данных. Big data, СУБД и SQL и noSQL