ORM также может означать: англ. Object Role Model, рус. Модель ролей объекта — методика концептуального проектирования информационных систем, включающая собственную графическую нотацию.
ORM (англ. Object-Relational Mapping, рус. объектно-реляционное отображение, или преобразование) — технология программирования, которая связывает базы данных с концепциями объектно-ориентированных языков программирования, создавая «виртуальную объектную базу данных». Существуют как проприетарные, так и свободные реализации этой технологии. Подход реализует концепцию «абстракция от хранилищ».
Необходимо обеспечить работу с данными в терминах классов, а не таблиц данных, и, напротив, преобразовать термины и данные классов в данные, пригодные для хранения в СУБД. В общем, необходимо избавиться от необходимости писать SQL-код для взаимодействия в СУБД.
Принцип действия Разработано множество пакетов, устраняющих необходимость в преобразовании объектов для хранения в реляционных базах данных.
Некоторые пакеты решают эту проблему, предоставляя библиотеки классов, способных выполнять такие преобразования автоматически. Имея список таблиц в базе данных и объектов в программе, они автоматически преобразуют запросы из одного вида в другой. В результате, например, запроса объекта «человек» необходимый SQL-запрос будет сформирован и выполнен, а результаты «волшебным» образом преобразованы в объекты «номер телефона» внутри программы.
С точки зрения программиста система должна выглядеть как постоянное хранилище объектов. Он может просто создавать объекты и работать с ними как обычно, а они автоматически будут сохраняться в реляционной базе данных.
На практике всё не так просто и очевидно. Все системы ORM обычно проявляют себя в том или ином виде, уменьшая в некотором роде возможность игнорирования базы данных. Более того, слой транзакций может быть медленным и неэффективным (особенно в терминах сгенерированного SQL). Все это может привести к тому, что программы будут работать медленнее и использовать больше памяти, чем программы, написанные «вручную».
Но ORM избавляет программиста от написания большого количества кода, часто однообразного и подверженного ошибкам, тем самым значительно повышая скорость разработки. Кроме того, большинство современных реализаций ORM позволяют программисту при необходимости самому жёстко задать код SQL-запросов, который будет использоваться при тех или иных действиях (сохранение в базу данных, загрузка, поиск и т. д.) с постоянным объектом.
Чтобы этот чудесный подход работал необходимо движение база данных -> ORM -> объекты. Наоборот это работает только на очень маленьких БД.
Оказывается, я 15 лет назад уже использовал этот подход, когда хранил данные экземпляров классов в файле, но я не догадался воткнуть между файлами и классами SQL. Наверное нужно предложить убрать прокладку в виде SQL и назвать это продвинутым ORM.
Sequelize — это ORM (Object-Relational Mapping — объектно-реляционное отображение или преобразование) для работы с такими СУБД (системами управления (реляционными) базами данных, Relational Database Management System, RDBMS), как Postgres, MySQL, MariaDB, SQLite и MSSQL. Это далеко не единственная ORM для работы с названными базами данных (далее — БД), но, лга одна из самых продвинутых и, что называется, “battle tested” (проверенных временем).
ORM хороши тем, что позволяют взаимодействовать с БД на языке приложения (JavaScript), т.е. без использования специально предназначенных для этого языков (SQL). Тем не менее, существуют ситуации, когда запрос к БД легче выполнить с помощью SQL (или можно выполнить только c помощью него).
1
2
yarn add sequelize
npm i sequelize
Проверка подключения. По умолчанию после того, как установки соединения, оно остается открытым. Для его закрытия следует вызвать метод sequelize.close().
Модель — это абстракция, представляющая таблицу в БД.
Модель сообщает Sequelize несколько вещей о сущности (entity), которую она представляет: название таблицы, то, какие колонки она содержит (и их типы данных) и др.
У каждой модели есть название. Это название не обязательно должно совпадать с названием соответствующей таблицы. Обычно, модели именуются в единственном числе (например, User), а таблицы — во множественном (например, Users). Sequelize выполняет плюрализацию (перевод значения из единственного числа во множественное) автоматически.
Модели могут определяться двумя способами:
После определения, модель доступна через sequelize.model + название модели.
Автоматическую плюрализацию названия таблицы можно отключить с помощью настройки freezeTableName, как на слайде вверху или глобально, как на слайде внизу В этом случае таблица будет называться User.
Название таблицы может определяться в явном виде: В этом случае таблица будет называться Employees.
Синхронизация модели с таблицей:
Возвращает промис Синхронизация всех моделей Удаление таблицы
Удаление всех таблиц: Sequelize принимает настройку match с регулярным выражением, позволяющую определять группу синхронизируемых таблиц: Обратите внимание: вместо синхронизации в продакшне следует использовать миграции.
По умолчанию Sequelize автоматически добавляет в создаваемую модель поля createAt и updatedAt с типом DataTypes.DATE. Это можно изменить как на слайде слева. Названные поля можно отключать по отдельности и переименовывать как на слайде справа.
Если для колонки определяется только тип данных, синтаксис определения атрибута может быть сокращен следующим образом как на слайде слева: По умолчанию значением колонки является NULL. Это можно изменить с помощью настройки defaultValue (определив “дефолтное” значение) как на слайде справа сверху: В качестве дефолтных могут использоваться специальные значения как на слайде справа внизу.
UUID может генерироваться автоматически:
Наш начальный код будет выглядеть следующим образом:
Также имеется возможность автоматического уменьшения значений полей (decrement()).
Настройка where позволяет выполнять фильтрацию возвращаемых данных. Существует большое количество операторов, которые могут использоваться совместно с where через Op.
Передача массива в where приводит к неявному применению оператора IN:
Операторы Op.and, Op.or и Op.not могут использоваться для создания сложных операций, связанных с логическими сравнениями:
Настройка order определяет порядок сортировки возвращаемых объектов:
Синтаксис группировки идентичен синтаксису сортировки, за исключением того, что при группировке не указывается направление. Кроме того, синтаксис группировки может быть сокращен до строки как на слайде сверху Настройки limit и offset позволяют ограничивать и/или пропускать определенное количество возвращаемых объектов как на слайде снизу
Sequelize предоставляет несколько полезных утилит:
Настройка raw со значением true отключает “оборачивание” ответа, возвращаемого SELECT, в экземпляр модели.
findOrCreate() — возвращает или создает и возвращает экземпляр, а также логическое значение — индикатор создания экземпляра. Настройка defaults используется для определения значений по умолчанию. При ее отсутствии, для заполнения полей используется значение, указанное в условии как на слайде внизу
findAndCountAll() — комбинация findAll() и count. Может быть полезным при использовании настроек limit и offset, когда мы хотим знать точное число записей, совпадающих с запросом. Возвращает объект с двумя свойствами: count — количество записей, совпадающих с запросом (целое число) rows — массив объектов
Sequelize позволяет определять геттеры и сеттеры для атрибутов моделей, а также виртуальные атрибуты — атрибуты, которых не существует в таблице и которые заполняются или наполняются (имеется ввиду популяция) Serquelize автоматически. Последние могут использоваться, например, для упрощения кода.
Геттер — это функция get(), определенная для колонки как в верхней части слайда
Геттер вызывается автоматически при чтении поля.
Обратите внимание: для получения значения поля в геттере мы использовали метод getDataValue(). Если вместо этого указать this.username, то мы попадем в бесконечный цикл.
Сеттер — это функция set(), определенная для колонки. Она принимает значение для установки как в нижней части слайда
Сеттер вызывается автоматически при создании экземпляра. В сеттере можно использовать значения других полей:
Геттеры и сеттеры можно использовать совместно. Допустим, что у нас имеется модель Post с полем content неограниченной длины, и в целях экономии памяти мы решили хранить в БД содержимое поста в сжатом виде. Обратите внимание: многие современные БД выполняют сжатие (компрессию) данных автоматически.
Представим, что у нас имеется модель User с полями firstName и lastName, и мы хотим получать полное имя пользователя. Для этого мы можем создать виртуальный атрибут со специальным типом DataTypes.VIRTUAL: В таблице не будет колонки fullName, однако мы сможем получать значение этого поля, как если бы оно существовало на самом деле.
Наша моделька будет выглядеть так: Отличие между выполнением валидации и применением или наложением органичение на значение поля состоит в следующем:
валидация выполняется на уровне Sequelize; для ее выполнения можно использовать любую функцию, как встроенную, так и кастомную; при провале валидации, SQL-запрос в БД не отправляется; ограничение определяется на уровне SQL; примером ограничения является настройка unique; при провале ограничения, запрос в БД все равно отправляется
В приведенном примере мы ограничили уникальность имени пользователя с помощью настройки unique. При попытке записать имя пользователя, которое уже существует в БД, возникнет ошибка SequelizeUniqueConstraintError.
По умолчанию колонки таблицы могут быть пустыми (нулевыми). Настройка allowNull со значением false позволяет это запретить. Обратите внимание: без установки данной настройки хотя бы для одного поля, можно будет выполнить такой запрос: User.create({}).
Валидаторы позволяют проводить проверку в отношении каждого атрибута модели. Валидация автоматически выполняется при запуске методов create(), update() и save(). Ее также можно запустить вручную с помощью validate().
Как было отмечено ранее, мы можем определять собственные валидаторы или использовать встроенные (предоставляемые библиотекой validator.js).
Для кастомизации сообщения об ошибке можно использовать объект со свойством msg как на слайде сверху
В этом случае для указания аргументов используется свойство args. Как на слайде по средедине
Для поля, которое может иметь значение null, встроенные валидаторы пропускаются. Это означает, что мы, например, можем определить поле, которое либо должно содержать строку длиной 5-10 символов, либо должно быть пустым: Как внизу на слйде
Обратите внимание, что для нулевых полей кастомные валидаторы выполняются:
Мы можем выполнять валидацию не только отдельных полей, но и модели в целом. В следующем примере мы проверяем наличие или отсутствии как поля latitude, так и поля longitude (либо должны быть указаны оба поля, либо не должно быть указано ни одного):
sequelize.query() позволяет выполнять необработанные SQL-запросы (raw queries). По умолчанию данная функция возвращает массив с результатами и объект с метаданными, при этом, содержание последнего зависит от используемого диалекта. На слайде вверху
Если нам не нужны метаданные, для правильного форматирования результата можно воспользоваться специальными типами запроса (query types). В середине слайда
Для привязки результатов необработанного запроса к модели используются настройки model и, опционально, mapToModel:. Внизу слайда
Если название атрибута в таблице содержит точки, то результирующий объект может быть преобразован во вложенные объекты с помощью настройки nest.
Без nest: true: на слайде вверху
С nest: true: на слайде внизу
Замены при выполнении запроса могут производиться двумя способами:
с помощью именованных параметров (начинающихся с :) с помощью неименованных параметров (представленных ?)
Заменители (placeholders) передаются в настройку replacements в виде массива (для неименованных параметров) или в виде объекта (для именованных параметров):
если передан массив, ? заменяется элементами массива в порядке их следования если передан объект, :key заменяются ключами объекта. При отсутствии в объекте ключей для заменяемых значений, а также в случае, когда ключей в объекте больше, чем заменяемых значений, выбрасывается исключение
Более сложные примеры замены в нижней части слайде
Кроме замены, можно выполнять привязку (bind) параметров. Привязка похожа на замену, но заменители обезвреживаются (escaped) и вставляются в запрос, отправляемый в БД, а связанные параметры отправляются в БД по отдельности. Связанные параметры обозначаются с помощью $число или $строка:
если передан массив, $1 будет указывать на его первый элемент (bind[0]) если передан объект, $key будет указывать на object[‘key’]. Каждый ключ объекта должен начинаться с буквы. $1 является невалидным ключом, даже если существует object[‘1’] в обоих случаях для сохранения знака $ может использоваться $$
Связанные параметры не могут быть ключевыми словами SQL, названиями таблиц или колонок. Они игнорируются внутри текста, заключенного в кавычки. Кроме того, в postgres может потребоваться указывать тип связываемого параметра в случае, когда он не может быть выведен на основании контекста — $1::varchar.