# ORM

> Объектно-реляционное отображение — библиотеки, которые связывают классы программы с таблицами реляционной БД и строят SQL-запросы из кода; удобство и переносимость в обмен на риск N+1 запросов и скрытой сложности.

Источник: https://llmdocs.ru/wiki/orm/ · обновлено 2026-11-09 · лицензия CC BY-SA 4.0

- **Назначение**: отображение объектов кода на строки таблиц
- **Паттерны**: Active Record, Data Mapper, Unit of Work
- **Python**: SQLAlchemy, Django ORM, SQLModel, Tortoise, Peewee
- **JavaScript/TypeScript**: Prisma, Drizzle, TypeORM, Sequelize
- **Go и Rust**: GORM, ent, sqlc (генератор); Diesel, SeaORM, sqlx (без ORM)
- **Java и .NET**: Hibernate (JPA), Entity Framework
**ORM** (англ. *object-relational mapping* — объектно-реляционное отображение) — техника и библиотеки, которые связывают классы и объекты в коде программы с таблицами и строками [реляционной базы данных](https://llmdocs.ru/wiki/relational-database.md). Вместо того чтобы писать [SQL](https://llmdocs.ru/wiki/sql.md) вручную и разбирать результаты, разработчик работает с объектами: `user.orders`, `session.add(order)`, — а ORM генерирует запросы, подставляет параметры и преобразует типы. Популярные ORM — SQLAlchemy и Django ORM в Python, Hibernate в Java, Entity Framework в .NET, Prisma в TypeScript, GORM в Go.[1]

## Проблема, которую решает ORM

Объектная модель программы и реляционная модель БД устроены по-разному: у объектов есть наследование, вложенность, ссылки и коллекции, у таблиц — строки, внешние ключи и соединения. Это расхождение называют **объектно-реляционным несоответствием импеданса** (*impedance mismatch*). ORM берёт на себя рутину на стыке: построение запросов, отслеживание изменённых объектов, транзакции, загрузку связанных данных и преобразование типов (даты, decimal, JSON, перечисления).

## Паттерны

Мартин Фаулер описал два основных подхода:[1]

- **Active Record** — объект сам знает, как себя сохранять: `post.save()`, `Post.objects.filter(...)`. Просто и наглядно; так устроены Django ORM, Ruby on Rails ActiveRecord, Eloquent в Laravel.
- **Data Mapper** — объекты ничего не знают о базе, сохранением занимается отдельный слой (сессия, репозиторий). Гибче для сложных доменных моделей; так устроены SQLAlchemy, Hibernate, Doctrine.

Вместе с Data Mapper обычно используется **Unit of Work**: сессия копит изменения объектов и записывает их одной [транзакцией](https://llmdocs.ru/wiki/transaction-acid.md) при `commit()`.

## Пример: SQLAlchemy 2.0

```python
from sqlalchemy import ForeignKey, String, create_engine, select
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, Session

class Base(DeclarativeBase): pass

class User(Base):
    __tablename__ = "users"
    id: Mapped[int] = mapped_column(primary_key=True)
    email: Mapped[str] = mapped_column(String(255), unique=True)
    orders: Mapped[list["Order"]] = relationship(back_populates="user")

class Order(Base):
    __tablename__ = "orders"
    id: Mapped[int] = mapped_column(primary_key=True)
    user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
    total: Mapped[int]
    user: Mapped[User] = relationship(back_populates="orders")

engine = create_engine("postgresql+psycopg://app@localhost/shop")
with Session(engine) as session:
    stmt = select(User).where(User.email.like("%@example.com"))
    for user in session.scalars(stmt):
        print(user.email, len(user.orders))
```

Аннотации `Mapped[...]` используют [типизацию Python](https://llmdocs.ru/wiki/type-hints.md), поэтому модели проверяются mypy и подсказываются IDE.[2] В [FastAPI](https://llmdocs.ru/wiki/fastapi.md) популярен SQLModel — надстройка, объединяющая модели SQLAlchemy и [Pydantic](https://llmdocs.ru/wiki/dataclasses-pydantic.md).

## Достоинства

- **Меньше шаблонного кода** — CRUD-операции в одну строку.
- **Безопасность** — запросы параметризуются автоматически, что защищает от [SQL-инъекций](https://llmdocs.ru/wiki/sql-injection.md).
- **Переносимость** — один код для [PostgreSQL](https://llmdocs.ru/wiki/postgresql.md), [MySQL](https://llmdocs.ru/wiki/mysql-mariadb.md), [SQLite](https://llmdocs.ru/wiki/sqlite.md) (в тестах удобно подменить базу).
- **Типы и автодополнение**, рефакторинг через IDE.
- **Интеграция с миграциями** — изменения моделей превращаются в [миграции](https://llmdocs.ru/wiki/schema-migrations.md) (Alembic, Django migrations, Prisma Migrate).

## Недостатки и ловушки

**Проблема N+1 запросов.** Самая частая ошибка: при обходе списка ORM лениво подгружает связанные объекты отдельным запросом для каждого элемента.

```python
for user in session.scalars(select(User)):   # 1 запрос
    print(len(user.orders))                  # + N запросов
```

Решение — явная «жадная» загрузка: `selectinload(User.orders)` или `joinedload` в SQLAlchemy, `select_related` и `prefetch_related` в [Django](https://llmdocs.ru/wiki/django.md),[4] `include` в Prisma.[5] Обнаружить проблему помогает логирование SQL (`echo=True`) и инструменты вроде django-debug-toolbar.

Другие проблемы:

- **Скрытая сложность** — непонятно, какой SQL будет выполнен; неэффективные запросы обнаруживаются только в продакшене. Нужно смотреть сгенерированный SQL и его план (см. [EXPLAIN и план запроса](https://llmdocs.ru/wiki/explain.md)).
- **Протекающая абстракция** — для сложных отчётов, оконных функций, `UPSERT`, CTE и специфичных возможностей СУБД всё равно приходится спускаться к SQL.
- **Наименьший общий знаменатель** — ради переносимости легко отказаться от сильных сторон конкретной базы.
- **Массовые операции** через объекты медленны: загрузить миллион объектов, изменить и сохранить — намного дороже одного `UPDATE`.

Тед Ньюард в 2006 году назвал ORM «Вьетнамом информатики» — болотом, в котором проект увязает по мере роста.[6] Сегодня позиция сообщества спокойнее: ORM хороши для типового CRUD, а для сложных запросов используют SQL.

## Альтернативы

- **Построители запросов** (query builders) — SQLAlchemy Core, Knex, Kysely, Drizzle, jOOQ: SQL-подобный код с типами и параметрами, без отображения объектов.
- **Генераторы кода из SQL** — sqlc для [Go](https://llmdocs.ru/wiki/go.md) пишет типизированные функции по SQL-файлам,[7] sqlx в Rust проверяет запросы против базы во время компиляции.
- **«Сырой» SQL** через драйвер с параметрами — максимальный контроль.
- **Micro-ORM** (Dapper в .NET) — только маппинг результатов в объекты.

Нередко в одном проекте ORM используется для обычных операций, а критичные по производительности запросы пишутся на SQL.

## См. также

- [SQL](https://llmdocs.ru/wiki/sql.md)
- [Миграции схемы БД](https://llmdocs.ru/wiki/schema-migrations.md)
- [Django](https://llmdocs.ru/wiki/django.md)
- [Индекс (базы данных)](https://llmdocs.ru/wiki/database-index.md)

## Источники

1. [Martin Fowler — Patterns of Enterprise Application Architecture (Active Record, Data Mapper, Unit of Work)](https://martinfowler.com/eaaCatalog/)
2. [SQLAlchemy 2.0 documentation — ORM Quick Start](https://docs.sqlalchemy.org/en/20/orm/quickstart.html)
3. [Django — Making queries](https://docs.djangoproject.com/en/stable/topics/db/queries/)
4. [Django — Database access optimization](https://docs.djangoproject.com/en/stable/topics/db/optimization/)
5. [Prisma documentation](https://www.prisma.io/docs)
6. [Ted Neward — The Vietnam of Computer Science (2006)](https://www.odbms.org/wp-content/uploads/2013/11/031.01-Neward-The-Vietnam-of-Computer-Science-June-2006.pdf)
7. [sqlc documentation](https://docs.sqlc.dev/)
