Работа с БД

Cassandra

ORM

CouchDB

ORM

LevelDB

ORM

MySQL

ORM

MongoDB

ORM

Neo4j

ORM

PostgreSQL

ORM

Redis

ORM

SQLite

ORM

ElasticSearch

ORM

Что такое ORM?

ORM также может означать: англ. Object Role Model, рус. Модель ролей объекта — методика концептуального проектирования информационных систем, включающая собственную графическую нотацию.

ORM (англ. Object-Relational Mapping, рус. объектно-реляционное отображение, или преобразование) — технология программирования, которая связывает базы данных с концепциями объектно-ориентированных языков программирования, создавая «виртуальную объектную базу данных». Существуют как проприетарные, так и свободные реализации этой технологии. Подход реализует концепцию «абстракция от хранилищ».

ORM

Необходимо обеспечить работу с данными в терминах классов, а не таблиц данных, и, напротив, преобразовать термины и данные классов в данные, пригодные для хранения в СУБД. В общем, необходимо избавиться от необходимости писать SQL-код для взаимодействия в СУБД.

Принцип действия Разработано множество пакетов, устраняющих необходимость в преобразовании объектов для хранения в реляционных базах данных.

Некоторые пакеты решают эту проблему, предоставляя библиотеки классов, способных выполнять такие преобразования автоматически. Имея список таблиц в базе данных и объектов в программе, они автоматически преобразуют запросы из одного вида в другой. В результате, например, запроса объекта «человек» необходимый SQL-запрос будет сформирован и выполнен, а результаты «волшебным» образом преобразованы в объекты «номер телефона» внутри программы.

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

На практике всё не так просто и очевидно. Все системы ORM обычно проявляют себя в том или ином виде, уменьшая в некотором роде возможность игнорирования базы данных. Более того, слой транзакций может быть медленным и неэффективным (особенно в терминах сгенерированного SQL). Все это может привести к тому, что программы будут работать медленнее и использовать больше памяти, чем программы, написанные «вручную».

Но ORM избавляет программиста от написания большого количества кода, часто однообразного и подверженного ошибкам, тем самым значительно повышая скорость разработки. Кроме того, большинство современных реализаций ORM позволяют программисту при необходимости самому жёстко задать код SQL-запросов, который будет использоваться при тех или иных действиях (сохранение в базу данных, загрузка, поиск и т. д.) с постоянным объектом.

Чтобы этот чудесный подход работал необходимо движение база данных -> ORM -> объекты. Наоборот это работает только на очень маленьких БД.

Оказывается, я 15 лет назад уже использовал этот подход, когда хранил данные экземпляров классов в файле, но я не догадался воткнуть между файлами и классами SQL. Наверное нужно предложить убрать прокладку в виде SQL и назвать это продвинутым ORM.

Основы Sequelize

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

Подключение к БД

ORM

Проверка подключения. По умолчанию после того, как установки соединения, оно остается открытым. Для его закрытия следует вызвать метод sequelize.close().

ORM

Модели

Модель — это абстракция, представляющая таблицу в БД.

Модель сообщает Sequelize несколько вещей о сущности (entity), которую она представляет: название таблицы, то, какие колонки она содержит (и их типы данных) и др.

У каждой модели есть название. Это название не обязательно должно совпадать с названием соответствующей таблицы. Обычно, модели именуются в единственном числе (например, User), а таблицы — во множественном (например, Users). Sequelize выполняет плюрализацию (перевод значения из единственного числа во множественное) автоматически.

Модели могут определяться двумя способами:

После определения, модель доступна через sequelize.model + название модели.

Определение моделей

ORM

ORM

Плюрализация названия

Автоматическую плюрализацию названия таблицы можно отключить с помощью настройки freezeTableName, как на слайде вверху или глобально, как на слайде внизу В этом случае таблица будет называться User.

ORM

Явное задание названия

Название таблицы может определяться в явном виде: В этом случае таблица будет называться Employees.

ORM

Запись изменений в БД

Синхронизация модели с таблицей:

Возвращает промис Синхронизация всех моделей Удаление таблицы

ORM

Удаление всех таблиц

Удаление всех таблиц: Sequelize принимает настройку match с регулярным выражением, позволяющую определять группу синхронизируемых таблиц: Обратите внимание: вместо синхронизации в продакшне следует использовать миграции.

ORM

Автоматические поля

По умолчанию Sequelize автоматически добавляет в создаваемую модель поля createAt и updatedAt с типом DataTypes.DATE. Это можно изменить как на слайде слева. Названные поля можно отключать по отдельности и переименовывать как на слайде справа.

ORM

ORM

Задание типа поля

Если для колонки определяется только тип данных, синтаксис определения атрибута может быть сокращен следующим образом как на слайде слева: По умолчанию значением колонки является NULL. Это можно изменить с помощью настройки defaultValue (определив “дефолтное” значение) как на слайде справа сверху: В качестве дефолтных могут использоваться специальные значения как на слайде справа внизу.

ORM

ORM

Типы данных

ORM

ORM

ORM

ORM

ORM

UUID

UUID может генерироваться автоматически:

ORM

Настройка колонки.

ORM

ORM

ORM

Экземпляры

Наш начальный код будет выглядеть следующим образом:

ORM

Создание и обновление экземпляра

ORM

Удаление и перезагрузка экземпляра

ORM

ORM

Сохранение отдельных полей

ORM

Автоматическое увеличение значения поля

ORM

Автоматическое увеличение значения нескольких полей

ORM

Также имеется возможность автоматического уменьшения значений полей (decrement()).

Основы выполнения запросов

Создание экземпляра

ORM

Создание экземпляра с определёнными полями

ORM

Получение экземпляра

ORM

Выборка полей

ORM

ORM

Настройка where позволяет выполнять фильтрацию возвращаемых данных. Существует большое количество операторов, которые могут использоваться совместно с where через Op.

ORM

ORM

Операторы

ORM

ORM

Передача массива в where приводит к неявному применению оператора IN:

ORM

Операторы Op.and, Op.or и Op.not могут использоваться для создания сложных операций, связанных с логическими сравнениями:

ORM

ORM

Более сложные запросы

ORM

ORM

ORM

Обновление экземпляра

ORM

Удаление экземпляра

ORM

Создание нескольких экземпляров одновременно

ORM

Сортировка и группировка

Настройка order определяет порядок сортировки возвращаемых объектов:

ORM

ORM

Синтаксис группировки идентичен синтаксису сортировки, за исключением того, что при группировке не указывается направление. Кроме того, синтаксис группировки может быть сокращен до строки как на слайде сверху Настройки limit и offset позволяют ограничивать и/или пропускать определенное количество возвращаемых объектов как на слайде снизу

ORM

ORM

Полезные операторы

Sequelize предоставляет несколько полезных утилит:

ORM

Поисковые запросы

Настройка raw со значением true отключает “оборачивание” ответа, возвращаемого SELECT, в экземпляр модели.

ORM

ORM

findOrCreate() — возвращает или создает и возвращает экземпляр, а также логическое значение — индикатор создания экземпляра. Настройка defaults используется для определения значений по умолчанию. При ее отсутствии, для заполнения полей используется значение, указанное в условии как на слайде внизу

ORM

findAndCountAll()

findAndCountAll() — комбинация findAll() и count. Может быть полезным при использовании настроек limit и offset, когда мы хотим знать точное число записей, совпадающих с запросом. Возвращает объект с двумя свойствами: count — количество записей, совпадающих с запросом (целое число) rows — массив объектов

ORM

Геттеры, сеттеры и виртуальные атрибуты

Sequelize позволяет определять геттеры и сеттеры для атрибутов моделей, а также виртуальные атрибуты — атрибуты, которых не существует в таблице и которые заполняются или наполняются (имеется ввиду популяция) Serquelize автоматически. Последние могут использоваться, например, для упрощения кода.

Геттер — это функция get(), определенная для колонки как в верхней части слайда

ORM

Геттер вызывается автоматически при чтении поля.

Обратите внимание: для получения значения поля в геттере мы использовали метод getDataValue(). Если вместо этого указать this.username, то мы попадем в бесконечный цикл.

Сеттер — это функция set(), определенная для колонки. Она принимает значение для установки как в нижней части слайда

ORM

Автоматический вызов сеттера

Сеттер вызывается автоматически при создании экземпляра. В сеттере можно использовать значения других полей:

ORM

Совместное использование

Геттеры и сеттеры можно использовать совместно. Допустим, что у нас имеется модель Post с полем content неограниченной длины, и в целях экономии памяти мы решили хранить в БД содержимое поста в сжатом виде. Обратите внимание: многие современные БД выполняют сжатие (компрессию) данных автоматически.

ORM

Сжатие данных

Представим, что у нас имеется модель User с полями firstName и lastName, и мы хотим получать полное имя пользователя. Для этого мы можем создать виртуальный атрибут со специальным типом DataTypes.VIRTUAL: В таблице не будет колонки fullName, однако мы сможем получать значение этого поля, как если бы оно существовало на самом деле.

ORM

Валидация и ограничения

Наша моделька будет выглядеть так: Отличие между выполнением валидации и применением или наложением органичение на значение поля состоит в следующем:

валидация выполняется на уровне Sequelize; для ее выполнения можно использовать любую функцию, как встроенную, так и кастомную; при провале валидации, SQL-запрос в БД не отправляется; ограничение определяется на уровне SQL; примером ограничения является настройка unique; при провале ограничения, запрос в БД все равно отправляется

В приведенном примере мы ограничили уникальность имени пользователя с помощью настройки unique. При попытке записать имя пользователя, которое уже существует в БД, возникнет ошибка SequelizeUniqueConstraintError.

По умолчанию колонки таблицы могут быть пустыми (нулевыми). Настройка allowNull со значением false позволяет это запретить. Обратите внимание: без установки данной настройки хотя бы для одного поля, можно будет выполнить такой запрос: User.create({}).

Валидаторы позволяют проводить проверку в отношении каждого атрибута модели. Валидация автоматически выполняется при запуске методов create(), update() и save(). Ее также можно запустить вручную с помощью validate().

ORM

validator.js

Как было отмечено ранее, мы можем определять собственные валидаторы или использовать встроенные (предоставляемые библиотекой validator.js).

ORM

ORM

Кастомизация сообщения об ошибке

Для кастомизации сообщения об ошибке можно использовать объект со свойством msg как на слайде сверху

ORM

В этом случае для указания аргументов используется свойство args. Как на слайде по средедине

ORM

Для поля, которое может иметь значение null, встроенные валидаторы пропускаются. Это означает, что мы, например, можем определить поле, которое либо должно содержать строку длиной 5-10 символов, либо должно быть пустым: Как внизу на слйде

ORM

Кастомные валидаторы для нулевых полей

Обратите внимание, что для нулевых полей кастомные валидаторы выполняются:

ORM

Валидация модели в целом

Мы можем выполнять валидацию не только отдельных полей, но и модели в целом. В следующем примере мы проверяем наличие или отсутствии как поля latitude, так и поля longitude (либо должны быть указаны оба поля, либо не должно быть указано ни одного):

ORM

Необработанные запросы

sequelize.query() позволяет выполнять необработанные SQL-запросы (raw queries). По умолчанию данная функция возвращает массив с результатами и объект с метаданными, при этом, содержание последнего зависит от используемого диалекта. На слайде вверху

ORM

Если нам не нужны метаданные, для правильного форматирования результата можно воспользоваться специальными типами запроса (query types). В середине слайда

ORM

Для привязки результатов необработанного запроса к модели используются настройки model и, опционально, mapToModel:. Внизу слайда

ORM

Пример использования других настроек

ORM

ORM

Настройка nest

Если название атрибута в таблице содержит точки, то результирующий объект может быть преобразован во вложенные объекты с помощью настройки nest.

Без nest: true: на слайде вверху

ORM

С nest: true: на слайде внизу

ORM

Замены в запросе

Замены при выполнении запроса могут производиться двумя способами:

с помощью именованных параметров (начинающихся с :) с помощью неименованных параметров (представленных ?)

Заменители (placeholders) передаются в настройку replacements в виде массива (для неименованных параметров) или в виде объекта (для именованных параметров):

если передан массив, ? заменяется элементами массива в порядке их следования если передан объект, :key заменяются ключами объекта. При отсутствии в объекте ключей для заменяемых значений, а также в случае, когда ключей в объекте больше, чем заменяемых значений, выбрасывается исключение

ORM

Более сложные примеры замены в нижней части слайде

ORM

Привязка параметров

Кроме замены, можно выполнять привязку (bind) параметров. Привязка похожа на замену, но заменители обезвреживаются (escaped) и вставляются в запрос, отправляемый в БД, а связанные параметры отправляются в БД по отдельности. Связанные параметры обозначаются с помощью $число или $строка:

если передан массив, $1 будет указывать на его первый элемент (bind[0]) если передан объект, $key будет указывать на object[‘key’]. Каждый ключ объекта должен начинаться с буквы. $1 является невалидным ключом, даже если существует object[‘1’] в обоих случаях для сохранения знака $ может использоваться $$

Связанные параметры не могут быть ключевыми словами SQL, названиями таблиц или колонок. Они игнорируются внутри текста, заключенного в кавычки. Кроме того, в postgres может потребоваться указывать тип связываемого параметра в случае, когда он не может быть выведен на основании контекста — $1::varchar.

ORM

ORM

ORM