Что такое TypeORM и зачем она нужна?
TypeORM — это мощная объектно-реляционная библиотека (ORM) для TypeScript и JavaScript, которая работает с различными базами данных: PostgreSQL, MySQL, MariaDB, SQLite, MS SQL Server, Oracle и MongoDB (частично). Она предоставляет удобный способ взаимодействия с реляционными базами данных через объектно-ориентированный подход, позволяя разработчикам работать с таблицами как с классами, а с записями — как с объектами.
Основные причины использовать TypeORM:
- Типизация: Полная интеграция с TypeScript обеспечивает строгую типизацию и автодополнение в IDE.
- Декларативный подход: Определение схемы базы данных через декораторы или схемы.
- Автомиграции: Автоматическое создание и синхронизация схемы базы данных.
- Активная запись (Active Record) и Data Mapper: Поддержка обоих популярных паттернов.
- Поддержка связей: One-to-One, One-to-Many, Many-to-Many.
- Query Builder: Гибкий построитель запросов для сложных SQL-запросов.
- Кроссплатформенность: Работает с Node.js, браузерами, React Native и Ionic.
Установка TypeORM
Для установки TypeORM и необходимых драйверов базы данных используйте npm (или yarn):
// Установка TypeORM
npm install typeorm --save
// Установка reflect-metadata (обязательно для декораторов)
npm install reflect-metadata --save
// Установка драйвера для PostgreSQL
npm install pg --save
// Установка драйвера для MySQL/MariaDB
npm install mysql2 --save
// Установка драйвера для SQLite
npm install sqlite3 --save
// Установка драйвера для SQL Server
npm install mssql --save
// Установка драйвера для MongoDB (экспериментально)
npm install mongodb --save
После установки добавьте в tsconfig.json:
{
"compilerOptions": {
"emitDecoratorMetadata": true,
"experimentalDecorators": true
}
}
Быстрый старт с примером кода
Создадим простое приложение для управления пользователями с использованием SQLite (не требует установки сервера).
1. Создайте файл data-source.ts для конфигурации подключения:
import { DataSource } from "typeorm";
import { User } from "./entity/User";
export const AppDataSource = new DataSource({
type: "sqlite",
database: "database.sqlite",
synchronize: true,
logging: true,
entities: [User],
subscribers: [],
migrations: [],
});
// Инициализация подключения
AppDataSource.initialize()
.then(() => {
console.log("Data Source has been initialized!");
})
.catch((err) => {
console.error("Error during Data Source initialization:", err);
});
2. Создайте сущность User в файле entity/User.ts:
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
firstName: string;
@Column()
lastName: string;
@Column({ unique: true })
email: string;
@Column({ default: true })
isActive: boolean;
}
3. Создайте файл index.ts для демонстрации работы:
import "reflect-metadata";
import { AppDataSource } from "./data-source";
import { User } from "./entity/User";
async function main() {
await AppDataSource.initialize();
// Создаем нового пользователя
const user = new User();
user.firstName = "Иван";
user.lastName = "Петров";
user.email = "ivan@example.com";
user.isActive = true;
// Сохраняем в базу данных
await AppDataSource.manager.save(user);
console.log("Пользователь сохранен с ID:", user.id);
// Получаем всех пользователей
const users = await AppDataSource.manager.find(User);
console.log("Все пользователи:", users);
// Получаем пользователя по ID
const foundUser = await AppDataSource.manager.findOneBy(User, { id: 1 });
console.log("Найденный пользователь:", foundUser);
// Обновляем пользователя
if (foundUser) {
foundUser.lastName = "Сидоров";
await AppDataSource.manager.save(foundUser);
console.log("Пользователь обновлен");
}
// Удаляем пользователя
await AppDataSource.manager.delete(User, { id: 1 });
console.log("Пользователь удален");
await AppDataSource.destroy();
}
main().catch(console.error);
4. Запустите приложение:
npx ts-node index.ts
Основные методы и функции TypeORM
1. DataSource.initialize()
Что делает: Инициализирует подключение к базе данных. Должен быть вызван перед любыми операциями с БД.
Параметры: Нет (использует конфигурацию из DataSource).
Пример:
const dataSource = new DataSource({
type: "postgres",
host: "localhost",
port: 5432,
username: "test",
password: "test",
database: "test",
});
await dataSource.initialize();
console.log("Подключение установлено");
2. EntityManager.save(entity)
Что делает: Сохраняет сущность в базу данных. Если сущность новая — создает запись, если существующая — обновляет.
Параметры:
entity— экземпляр сущности или массив сущностейoptions(опционально) — объект с настройками
Пример:
const user = new User();
user.firstName = "Анна";
user.email = "anna@example.com";
await dataSource.manager.save(user);
// Сохранение нескольких сущностей
const users = [user1, user2, user3];
await dataSource.manager.save(users);
3. EntityManager.find(entityClass, options?)
Что делает: Находит все записи, соответствующие условиям.
Параметры:
entityClass— класс сущностиoptions(опционально) — объект с условиями поиска
Пример:
// Все пользователи
const allUsers = await dataSource.manager.find(User);
// С условиями
const activeUsers = await dataSource.manager.find(User, {
where: { isActive: true },
order: { firstName: "ASC" },
take: 10,
skip: 0,
});
// С выборкой полей
const partialUsers = await dataSource.manager.find(User, {
select: ["id", "email"],
});
4. EntityManager.findOneBy(entityClass, where)
Что делает: Находит одну запись по простым условиям.
Параметры:
entityClass— класс сущностиwhere— объект с условиями
Пример:
const user = await dataSource.manager.findOneBy(User, {
id: 1,
});
const userByEmail = await dataSource.manager.findOneBy(User, {
email: "test@example.com",
});
5. EntityManager.delete(entityClass, criteria)
Что делает: Удаляет записи, соответствующие критериям.
Параметры:
entityClass— класс сущностиcriteria— условия удаления (ID, объект или массив)
Пример:
// Удаление по ID
await dataSource.manager.delete(User, 1);
// Удаление по условию
await dataSource.manager.delete(User, { isActive: false });
// Удаление нескольких записей
await dataSource.manager.delete(User, [1, 2, 3]);
6. EntityManager.update(entityClass, criteria, partialEntity)
Что делает: Обновляет записи без полной загрузки сущности.
Параметры:
entityClass— класс сущностиcriteria— условия обновленияpartialEntity— частичные данные для обновления
Пример:
await dataSource.manager.update(User, 1, {
lastName: "НоваяФамилия",
isActive: false,
});
// Обновление по условию
await dataSource.manager.update(
User,
{ isActive: true },
{ isActive: false }
);
7. EntityManager.count(entityClass, options?)
Что делает: Возвращает количество записей, соответствующих условиям.
Параметры:
entityClass— класс сущностиoptions(опционально) — объект с условиями
Пример:
const totalUsers = await dataSource.manager.count(User);
const activeCount = await dataSource.manager.count(User, {
where: { isActive: true },
});
8. EntityManager.createQueryBuilder(entityClass, alias)
Что делает: Создает QueryBuilder для построения сложных SQL-запросов.
Параметры:
entityClass— класс сущностиalias— псевдоним для таблицы
Пример:
const users = await dataSource
.manager
.createQueryBuilder(User, "user")
.leftJoinAndSelect("user.posts", "post")
.where("user.isActive = :isActive", { isActive: true })
.andWhere("user.createdAt > :date", { date: new Date("2023-01-01") })
.orderBy("user.firstName", "ASC")
.addOrderBy("post.title", "DESC")
.take(20)
.skip(0)
.getMany();
9. Repository.find(options?)
Что делает: Работает аналогично EntityManager.find, но через репозиторий сущности.
Параметры: Те же, что и у EntityManager.find.
Пример:
const userRepository = dataSource.getRepository(User);
const users = await userRepository.find({
relations: ["posts"],
where: { isActive: true },
});
const user = await userRepository.findOne({
where: { id: 1 },
relations: ["profile"],
});
10. DataSource.transaction(callback)
Что делает: Выполняет операции в транзакции с возможностью отката.
Параметры:
callback— асинхронная функция, принимающая EntityManager
Пример:
await dataSource.transaction(async (manager) => {
const user = await manager.findOneBy(User, { id: 1 });
if (!user) {
throw new Error("Пользователь не найден");
}
user.balance -= 100;
await manager.save(user);
const order = new Order();
order.userId = user.id;
order.amount = 100;
await manager.save(order);
});
Реальный практический пример использования
Создадим систему управления заказами для интернет-магазина с использованием TypeORM и PostgreSQL.
Сущности:
// entity/Customer.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm";
import { Order } from "./Order";
@Entity()
export class Customer {