Перейти к основному содержимому

4.10. ORM

Разработчику Аналитику Тестировщику
Архитектору Инженеру

ORM

Основные понятия

ORM – это программный подход или инструмент, который позволяет преобразовывать данные между объектной моделью программы (объекты, классы) и реляционной моделью базы данных (таблицы, строки, столбцы).


Объектная модель - в ООП данные представлены в виде объектов, которые имеют свойства (атрибуты) и поведение (методы).


Реляционная модель - в реляционных базах данных, данные хранятся в таблицах, где строки представляют записи, а столбцы - атрибуты.


Задача ORM - вывести формулу:

Объектная модель - Реляционная модель.

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

ORM предоставляет уровень абстракции, который скрывает сложности работы с базой данных. Это означает, что программист может сосредоточиться на логике приложения, а не на деталях взаимодействия с БД.


Абстракция ORM

Как работает абстракция ORM?

  1. Классы как таблицы:
    • каждый класс в программе соответствует таблице в базе данных;
    • свойства класса соответствуют столбцам таблицы.
  2. Объекты как строки:
    • экземпляр класса (объект) представляет собой строку в таблице;
    • значения свойств объекта соответствуют значениям в ячейках строки.
  3. Методы для операций с данными. ORM предоставляет готовые методы для выполнения CRUD-операций (Create, Read, Update, Delete), которые автоматически преобразуются в SQL-запросы.
  4. Отношения между объектами. Базы не просто называются реляционными - важна связь. И ORM поддерживает отношения между объектами (например, один-ко-многим, многие-к-другим), которые отражаются в связях между таблицами в базе данных.

Пример абстракции.

Прямой SQL-запрос был бы таким:

SELECT * FROM Users WHERE Age > 30

Абстракция в C#, через LINQ-синтаксис (позже изучим):

var users = (from user in dbContext.Users
where user.Age > 30
select user).ToList();

А если использовать Entity Framework (прокаченную ORM на C#), то:

var users = dbContext.Users.Where(user => user.Age > 30).ToList();

Конечно, мы ещё не изучали особенности языка C#, но здесь важно понять следующее при разборе примеров:

  • dbContext.Users — это DbSet<User>, представляющий таблицу Users в БД.
  • .Where(...) — метод LINQ, который преобразуется в SQL-условие WHERE под капотом.
  • user => user.Age > 30 — лямбда-выражение, заменяющее условие WHERE Age > 30 (анонимная функция, которая фильтрует записи);
  • .ToList() – материализует запрос (выполняет SQL и возвращает список объектов).

Таким образом, если приводить соответствие:

Абстракция в ORM (C#)Эквивалент в SQL БД
Класс UsersТаблица Users
Свойства класса Users (Id, Name, Age)Столбцы таблицы Users (Id, Name, Age)
dbContext.UsersSELECT * FROM Users
.Where(user => user.Age > 30)WHERE Age > 30
.Select(user => user.Name)SELECT Name FROM Users
users.ToList()Выполнение запроса (Execute)
Переменная usersРезультат запроса SQL
Методы Where, From, SelectКлючевые слова WHERE, FROM, SELECT
Объект user (экземпляр класса)Запись (строка) в таблице

Следовательно, в базе данных есть таблица Users:

IdNameAddressEmail
1Иван Петровул. Ленина, 10ivan@mail.ru

Представим, что создавалась она так:

CREATE TABLE Users (
Id INT PRIMARY KEY IDENTITY(1,1), -- Автоинкрементный первичный ключ
Name NVARCHAR(100) NOT NULL, -- Имя пользователя
Address NVARCHAR(200) NULL, -- Адрес (может быть NULL)
Email NVARCHAR(100) NOT NULL -- Email (уникальный)
);

В коде мы бы создали класс User, со следующим содержимым:

[Table("Users")]
public class User
{
[Key] // Указывает, что это первичный ключ
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] // Автоинкремент
public int Id { get; set; }

[Required] // NOT NULL в БД
[MaxLength(100)] // Ограничение длины (NVARCHAR(100))
public string Name { get; set; }

[MaxLength(200)] // NULL в БД (по умолчанию, если не Required)
public string? Address { get; set; } // "?" означает, что может быть null

[Required]
[MaxLength(100)]
[EmailAddress] // Проверка формата email
public string Email { get; set; }
}


По итогу мы получаем сопоставление:

C# (Класс User)SQL (Таблица Users)
public int IdId INT PRIMARY KEY
public string NameName NVARCHAR(100)
public string? AddressAddress NVARCHAR(200) NULL
public string EmailEmail NVARCHAR(100)

В коде выше, конечно, добавили атрибуты вроде [Key], [Required] - они помогают ORM понять структуру БД. Но! Мы ещё не изучили C#, поэтому задача в вышеуказанном примере - на практике показать, что такое ORM.


Логическое представление

Вернёмся к теории.

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

Поэтому здесь работа идёт с объектами, а ORM сама заботится об SQL-запросах.

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

Примеры ORM для реляционных БД:

  • Hibernate (Java)
  • Entity Framework (C#)
  • SQLAlchemy (Python)

Но это не значит, что для нереляционных БД отсутствует ORM.

В нереляционном представлении, данные хранятся в более гибком формате (документы JSON, графы, ключ-значение). И для них тоже используется ORM. ORM адаптируется к специфике нереляционных баз данных, вместо таблиц используются коллекции, а вместо строк - документы.

Примеры ORM для нереляционных БД:

  • Mongoose (для MongoDB)
  • ODM (Object Document Mapping) — аналог ORM для документоориентированных БД.

В чём отличие между реляционным и нереляционным представлением в ORM?

ХарактеристикаРеляционное представлениеНереляционное представление
Структура данныхТаблицы, строки, столбцыДокументы, ключ-значение, графы
Связи между даннымиЧерез внешние ключиЧерез ссылки или вложенные структуры
Гибкость схемыЖесткая схемаГибкая схема
Пример ORMHibernate, Entity FrameworkMongoose, ODM