За кулисами: Как ОРМ "думает"?


Меня интересуют некоторые разработки, лежащие в основе Rails ActiveRecord, доктрины для PHP (и аналогичных ORM).

  • Как ORM удается реализовать такие функции, как цепные средства доступа, и насколько глубоко они обычно должны работать?
  • Как ORM создает внутренние запросы?
  • Как ORM управляет запросами, сохраняя при этом произвольный характер всего, что от него ожидается?

Очевидно, что это академический вопрос, но все природы ответы приветствуются!

(Мой язык выбора - OO PHP5.3!)

Author: Omega, 2009-07-21

4 answers

Вызовы цепных методов ортогональны вопросу ORM, они используются повсеместно в ООП. Цепочечный метод просто возвращает ссылку на текущий объект, позволяя вызывать возвращаемое значение. В PHP

class A {
    public function b() {
        ...
        return $this;
    }

    public function c($param) {
        ...
        return $this;
    }       
}


$foo = new A();
$foo->b()->c('one');
// chaining is equivilant to
// $foo = $foo->b();
// $foo = $foo->c();

Что касается того, как строятся запросы, существует два метода. В ActiveRecord, таких как ORMs, есть код, который проверяет метаданные базы данных. В большинстве баз данных есть какие-то команды SQL или SQL, подобные командам, для просмотра этих метаданных. (MySQL DESCRIBE TABLE, Oracle USER_TAB_COLUMNS таблица и т.д.)

В некоторых ORM вы описываете таблицы базы данных на нейтральном языке, таком как YAML. Другие могут вывести структуру базы данных из того, как вы создали свои объектные модели (я хочу сказать, что это делает Django, но прошло некоторое время с тех пор, как я смотрел на это). Наконец, существует гибридный подход, при котором используется любой из двух предыдущих методов, но предоставляется отдельный инструмент для автоматической генерации файлов YAML/etc. или классов.

Один из имен и типов данных a таблицы известны, довольно легко прагматично написать SQL-запрос, который возвращает все строки или определенный набор строк, соответствующих определенным критериям.

Что касается вашего последнего вопроса,

Как ORM управляет запросами, сохраняя при этом произвольный характер всего , что от него ожидается?

Я бы сказал, что ответ "не очень хорошо". Как только вы выйдете за рамки метафоры "один стол, один объект", у каждого ORM будет свой подход и философия относительно того, как Для моделирования объектов следует использовать SQL-запросы. Абстрактно, однако, это так же просто, как добавление новых методов, которые строят запросы на основе предположений ORM (т.Е. метод "findmanytomanyrowset" Zend_Db_Table)

 3
Author: Alan Storm, 2009-07-21 16:30:33

Как ORM удается реализовать такие функции, как цепные средства доступа, и насколько глубоко они обычно должны работать?

Похоже, никто на это не ответил. Я могу быстро описать, как Доктрина делает это в PHP.

В Доктрине ни одно из полей, которые вы видите в объектной модели, на самом деле не определено для этого класса. Итак, в вашем примере, $car->владельцы, в классе $car нет фактического поля под названием "владельцы", определенного в классе $car.

Вместо этого ORM использует магию такие методы, как __get и __set. Поэтому, когда вы используете выражение типа $car->color, PHP внутренне вызывает Doctrine_Record#__get("цвет").

На данный момент ORM может удовлетворить это в любом случае необходимо. Здесь есть много возможных дизайнов. Он может хранить эти значения, например, в массиве с именем $_values, а затем возвращать $this->_values['цвет']. Доктрина, в частности, отслеживает не только значения для каждой записи, но и ее статус относительно сохранения в база данных.

Один из примеров этого, который не является интуитивным, связан с отношениями Доктрины. Когда вы получаете ссылку на $car, она имеет отношение к таблице людей, которая называется "владельцы". Таким образом, данные для $car->владельцев фактически хранятся в отдельной таблице от данных для самого $car. Таким образом, у ORM есть два варианта:

  1. Каждый раз, когда вы загружаете пользователя $, ORM автоматически объединяет все связанные таблицы и заполняет эту информацию в объект. Теперь, когда вы делаете $car->владельцы, это данные уже есть. Однако этот метод является медленным, поскольку объекты могут иметь множество взаимосвязей, и эти взаимосвязи могут сами иметь взаимосвязи. Таким образом, вы бы добавили множество соединений и даже не обязательно использовали бы эту информацию.
  2. Каждый раз, когда вы загружаете пользователя $, ORM замечает, какие поля загружены из таблицы пользователей, и заполняет их, но любые поля, загруженные из связанных таблиц, не загружаются. Вместо этого некоторые метаданные прикрепляются к этим полям, чтобы отметьте их как "не загруженные, но доступные". Теперь, когда вы пишете выражение $car->владельцы, ORM видит, что связь "владельцы" не загружена, и выдает отдельный запрос, чтобы получить эту информацию, добавить ее в объект, а затем вернуть эти данные. Все это происходит прозрачно, и вам не нужно этого осознавать.

Конечно, в Доктрине используется #2, поскольку #1 становится громоздким для любого реального производственного участка с умеренной сложностью. Но у него также есть побочные эффекты. Если ты используете несколько отношений на $car, затем Doctrine загрузит каждое из них отдельно по мере доступа к нему. Таким образом, вы в конечном итоге выполняете 5-6 запросов, когда, возможно, требовался только 1.

Доктрина позволяет оптимизировать эту ситуацию с помощью языка запросов доктрины. Вы сообщаете DQL, что хотите загрузить объект car, но также присоединяете его к владельцам, производителю, названиям, залогам и т. Д., И он загрузит все эти данные в объекты.

Фью! Длинный ответ. В принципе, однако, вы добрались до суть вопроса "Какова цель ORM?" и "Почему мы должны его использовать?" ORM позволяет нам продолжать думать в объектном режиме в большинстве случаев, но абстракция не идеальна, и утечки в абстракции, как правило, приводят к снижению производительности.

 2
Author: Mark E. Haase, 2010-03-07 16:42:45

Я создал презентацию на тему создания PHP-карты данных, которая может быть вам интересна. Он был записан на видео в коворкинге в Оклахома-Сити, когда я представил его там для группы пользователей PHP:

Видео: http://blip.tv/file/2249586/

Слайды презентации: http://www.slideshare.net/vlucas/building-data-mapper-php5-presentation

Презентация была в основном ранней концепцией phpDataMapper, хотя с тех пор многое изменилось.

Надеюсь, они помогут вам немного лучше понять внутреннюю работу ORMS.

 1
Author: Vance Lucas, 2009-08-20 21:24:59

Цепные методы доступа на самом деле не имеют большого значения: вы return $this из метода setter. Бум, готово, работает на стольких уровнях, сколько вам нравится.

 0
Author: chaos, 2009-07-21 14:24:33