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

Сущности

Неофициальный Бета-перевод

Эта страница переведена PageTurner AI (бета). Не одобрена официально проектом. Нашли ошибку? Сообщить о проблеме →

Что такое сущность?

Сущность — это класс, который сопоставляется с таблицей базы данных (или коллекцией при использовании MongoDB). Вы можете создать сущность, определив новый класс и пометив его декоратором @Entity():

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"

@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number

@Column()
firstName: string

@Column()
lastName: string

@Column()
isActive: boolean
}

Это создаст следующую таблицу в базе данных:

+-------------+--------------+----------------------------+
| user |
+-------------+--------------+----------------------------+
| id | int | PRIMARY KEY AUTO_INCREMENT |
| firstName | varchar(255) | |
| lastName | varchar(255) | |
| isActive | boolean | |
+-------------+--------------+----------------------------+

Базовые сущности состоят из столбцов и связей. Каждая сущность ОБЯЗАТЕЛЬНО должна иметь первичный столбец (или столбец ObjectId при использовании MongoDB).

Каждая сущность должна быть зарегистрирована в настройках источника данных:

import { DataSource } from "typeorm"
import { User } from "./entities/User"

const myDataSource = new DataSource({
type: "mysql",
host: "localhost",
port: 3306,
username: "test",
password: "test",
database: "test",
entities: [User],
})

Или вы можете указать целую директорию со всеми сущностями — и все они будут загружены:

import { DataSource } from "typeorm"

const dataSource = new DataSource({
type: "mysql",
host: "localhost",
port: 3306,
username: "test",
password: "test",
database: "test",
entities: [__dirname + "/entities/**/*{.js,.ts}"],
})

Если вы хотите использовать альтернативное имя таблицы для сущности User, укажите его в @Entity: @Entity("my_users"). Чтобы установить базовый префикс для всех таблиц БД в приложении, используйте опцию entityPrefix в настройках источника данных.

Аргументы конструктора сущности должны быть необязательными, поскольку ORM создаёт экземпляры классов при загрузке из БД и не знает ваших параметров конструктора.

Подробнее о параметрах @Entity — в справочнике декораторов.

Столбцы сущности

Поскольку таблицы БД состоят из столбцов, ваши сущности тоже должны содержать столбцы. Каждое свойство класса сущности, помеченное @Column, будет сопоставлено со столбцом таблицы БД.

Первичные столбцы

Каждая сущность должна иметь минимум один первичный столбец. Существует несколько типов первичных столбцов:

  • @PrimaryColumn() создаёт первичный столбец, принимающий значение любого типа. Вы можете указать тип столбца явно. Если тип не указан — он будет выведен из типа свойства. В примере ниже будет создан столбец id с типом int, значение которого нужно назначать вручную перед сохранением.
import { Entity, PrimaryColumn } from "typeorm"

@Entity()
export class User {
@PrimaryColumn()
id: number
}
  • @PrimaryGeneratedColumn() создаёт первичный столбец с автоматической генерацией значения через автоинкремент. Будет создан столбец int с параметрами auto-increment/serial/sequence/identity (зависит от БД и конфигурации). Значение назначать вручную не требуется — оно генерируется автоматически.
import { Entity, PrimaryGeneratedColumn } from "typeorm"

@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
}
  • @PrimaryGeneratedColumn("uuid") создаёт первичный столбец со значением типа uuid (уникальная строковая идентификация). Значение генерируется автоматически — назначать его вручную не требуется.
import { Entity, PrimaryGeneratedColumn } from "typeorm"

@Entity()
export class User {
@PrimaryGeneratedColumn("uuid")
id: string
}

Также поддерживаются составные первичные столбцы:

import { Entity, PrimaryColumn } from "typeorm"

@Entity()
export class User {
@PrimaryColumn()
firstName: string

@PrimaryColumn()
lastName: string
}

При сохранении сущностей через save система всегда ищет сущность в БД по заданному id (или ids). Если id/ids найдены — соответствующая строка обновляется. Если совпадений нет — вставляется новая строка.

Для поиска сущности по id используйте manager.findOneBy или repository.findOneBy. Пример:

// find one by id with single primary key
const person = await dataSource.manager.findOneBy(Person, { id: 1 })
const person = await dataSource.getRepository(Person).findOneBy({ id: 1 })

// find one by id with composite primary keys
const user = await dataSource.manager.findOneBy(User, {
firstName: "Timber",
lastName: "Saw",
})
const user = await dataSource.getRepository(User).findOneBy({
firstName: "Timber",
lastName: "Saw",
})

Специальные столбцы

Доступны специальные типы столбцов с дополнительной функциональностью:

  • @CreateDateColumn — специальный столбец, автоматически устанавливающийся в дату создания сущности. Значение назначать не нужно — оно устанавливается автоматически.

  • @UpdateDateColumn — специальный столбец, автоматически обновляющийся при каждом вызове save (менеджер сущностей или репозиторий) или во время upsert при обновлении. Значение назначать не нужно — оно обновляется автоматически.

  • @DeleteDateColumn — специальный столбец, автоматически устанавливающийся в дату мягкого удаления сущности при вызове операции soft-delete через менеджер сущностей или репозиторий. Значение назначать не нужно — оно устанавливается автоматически. При наличии этого столбца область видимости по умолчанию — «не удалённые».

  • @VersionColumn — специальный столбец, автоматически увеличивающий номер версии сущности при каждом save или upsert с обновлением. Значение назначать не нужно — оно обновляется автоматически.

Типы колонок

TypeORM поддерживает все распространённые типы колонок, доступные в базах данных. Типы колонок зависят от конкретной СУБД — это обеспечивает гибкость в проектировании схемы базы данных.

Тип колонки можно указать первым параметром в @Column или через опции колонки в @Column. Например:

@Column("int")

или

@Column({ type: "int" })

Если вы хотите указать дополнительные параметры типа, вы можете сделать это через опции колонки. Например:

@Column("varchar", { length: 200 })

Примечание о типе bigint: В SQL-базах данных тип bigint не совместим со стандартным number и преобразуется в string.

Тип столбца enum

Тип enum поддерживается в postgres и mysql. Доступны различные варианты определения:

Использование TypeScript enum:

export enum UserRole {
ADMIN = "admin",
EDITOR = "editor",
GHOST = "ghost",
}

@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number

@Column({
type: "enum",
enum: UserRole,
default: UserRole.GHOST,
})
role: UserRole
}

Поддерживаются строковые, числовые и гетерогенные перечисления.

Использование массива со значениями enum:

export type UserRoleType = "admin" | "editor" | "ghost",

@Entity()
export class User {

@PrimaryGeneratedColumn()
id: number;

@Column({
type: "enum",
enum: ["admin", "editor", "ghost"],
default: "ghost"
})
role: UserRoleType
}

simple-array — тип колонки

Существует специальный тип колонки simple-array для хранения примитивных массивов в одной строковой колонке. Значения разделяются запятыми. Например:

@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number

@Column("simple-array")
names: string[]
}
const user = new User()
user.names = ["Alexander", "Alex", "Sasha", "Shurik"]

Будет храниться в одном столбце базы данных как значение Alexander,Alex,Sasha,Shurik. При загрузке данных из базы данных имена вернутся в виде массива, точно в таком же виде, в каком вы их сохранили.

Важно: Значения не должны содержать запятых.

Тип колонки simple-json

Специальный тип simple-json хранит любые значения через JSON.stringify. Полезен при отсутствии нативного JSON-типа в базе данных. Например:

@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number

@Column("simple-json")
profile: { name: string; nickname: string }
}
const user = new User()
user.profile = { name: "John", nickname: "Malkovich" }

Будет храниться в отдельной колонке базы данных как значение {"name":"John","nickname":"Malkovich"}. При загрузке данных из базы вы получите обратно ваш объект/массив/примитив с помощью JSON.parse.

Колонки с генерируемыми значениями

Вы можете создать колонку с автоматически генерируемым значением, используя декоратор @Generated. Например:

@Entity()
export class User {
@PrimaryColumn()
id: number

@Column()
@Generated("uuid")
uuid: string
}

Значение uuid будет автоматически сгенерировано и сохранено в базу данных.

Помимо "uuid" существуют типы генерации "increment", "identity" (только для Postgres 10+) и "rowid" (только для CockroachDB), однако на некоторых платформах баз данных есть ограничения для таких типов генерации (например, в некоторых БД может быть только одна автоинкрементная колонка, или автоинкремент должен быть первичным ключом).

Векторные колонки

Столбцы типа vector поддерживаются в MariaDB/MySQL, Microsoft SQL Server, PostgreSQL (через расширение pgvector) и SAP HANA Cloud, что позволяет хранить и запрашивать векторные эмбеддинги для поиска похожих объектов (similarity search) и машинного обучения.

TypeORM поддерживает оба типа колонок — vector и halfvec — в различных базах данных:

  • vector — хранит векторы как 4-байтные числа с плавающей точкой (одинарная точность)

    • MariaDB/MySQL: нативный тип vector
    • Microsoft SQL Server: нативный тип vector
    • PostgreSQL: тип vector через расширение pgvector
    • SAP HANA Cloud: псевдоним для типа real_vector
  • halfvec — хранит векторы как 2-байтные числа с плавающей точкой (половинная точность) для экономии памяти

    • PostgreSQL: тип halfvec через расширение pgvector
    • SAP HANA Cloud: псевдоним для типа half_vector

Количество размерностей вектора можно указать с помощью опции length:

@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number

// Vector without specified dimensions
@Column("vector")
embedding: number[] | Buffer

// Vector with 3 dimensions: vector(3)
@Column("vector", { length: 3 })
embedding_3d: number[] | Buffer

// Half-precision vector with 4 dimensions: halfvec(4) (works on PostgreSQL and SAP HANA only)
@Column("halfvec", { length: 4 })
halfvec_embedding: number[] | Buffer
}

Примечание:

  • MariaDB/MySQL: Поддержка векторов доступна с MariaDB 11.7 и MySQL 9
  • Microsoft SQL Server: Требуется SQL Server 2025 (17.x) или новее
  • PostgreSQL: Требуется установленное расширение pgvector (предоставляет типы данных и операторы схожести)
  • SAP HANA: Требуется SAP HANA Cloud (2024Q1+) и поддерживаемая версия @sap/hana-client

Пространственные столбцы

Microsoft SQL Server, MySQL/MariaDB, PostgreSQL/CockroachDB и SAP HANA поддерживают пространственные колонки. Поддержка TypeORM для каждой СУБД незначительно отличается, особенно из-за различий в именах колонок.

MS SQL, MySQL/MariaDB и SAP HANA используют геометрические данные в формате Well-Known Text (WKT), поэтому пространственные колонки должны помечаться типом string.

import { Entity, PrimaryColumn, Column } from "typeorm"

@Entity()
export class Thing {
@PrimaryColumn()
id: number

@Column("point")
point: string

@Column("linestring")
linestring: string
}

...

const thing = new Thing()
thing.point = "POINT(1 1)"
thing.linestring = "LINESTRING(0 0,1 1,2 2)"

Для Postgres/CockroachDB см. Типы данных PostGIS

Опции колонок

Опции колонок определяют дополнительные настройки для колонок вашей сущности. Вы можете указать опции колонки на @Column:

@Column({
type: "varchar",
length: 150,
unique: true,
// ...
})
name: string;

Список доступных опций в ColumnOptions:

  • type: ColumnType — Тип столбца. Один из типов, перечисленных выше.

  • name: string — Имя столбца в таблице БД. По умолчанию генерируется из имени свойства. Можно переопределить.

  • length: number — Длина типа столбца. Например, для создания varchar(150) укажите тип и эту опцию.

  • onUpdate: string — Триггер ON UPDATE. Используется только в MySQL.

  • nullable: boolean — Делает столбец NULL или NOT NULL в БД. По умолчанию nullable: false.

  • update: boolean - Указывает, обновляется ли значение столбца при операции "save". Если false, записать значение можно только при первоначальной вставке объекта. По умолчанию true.

  • insert: boolean - Указывает, устанавливается ли значение столбца при первой вставке объекта. По умолчанию true.

  • select: boolean - Определяет, скрывать ли этот столбец по умолчанию при запросах. При false данные столбца не будут возвращаться стандартным запросом. По умолчанию select: true.

  • default: string - Задаёт значение DEFAULT на уровне базы данных.

  • primary: boolean - Помечает столбец как первичный. Аналогично использованию @PrimaryColumn.

  • unique: boolean - Помечает столбец как уникальный (создаёт уникальное ограничение).

  • comment: string - Комментарий к столбцу в БД. Поддерживается не всеми типами баз данных.

  • precision: number - Точность для десятичных столбцов (только для exact numeric), определяющая максимальное количество хранимых цифр. Применяется в некоторых типах столбцов.

  • scale: number - Масштаб для десятичных столбцов (только для exact numeric), определяющий количество цифр после запятой (не может превышать precision). Применяется в некоторых типах столбцов.

  • unsigned: boolean - Добавляет атрибут UNSIGNED к числовому столбцу. Только для MySQL.

  • charset: string - Задаёт кодировку столбца. Поддерживается не всеми типами БД.

  • collation: string - Задаёт правила сортировки для столбца.

  • enum: string[]|AnyEnum - Для типа enum: список разрешённых значений. Можно указать массив значений или enum-класс.

  • enumName: string - Определяет имя используемого enum-типа.

  • asExpression: string - Выражение для generated column. Только для MySQL.

  • generatedType: "VIRTUAL"|"STORED" - Тип generated column. Только для MySQL.

  • hstoreType: "object"|"string" - Тип возвращаемого значения для HSTORE. Возвращает значение как строку или объект. Только для Postgres.

  • array: boolean - Для типов столбцов Postgres/CockroachDB, поддерживающих массивы (напр. int[]).

  • transformer: { from(value: DatabaseType): EntityType, to(value: EntityType): DatabaseType } - Для преобразования свойств произвольного типа EntityType в тип DatabaseType, поддерживаемый БД. Поддерживается массив трансформеров — они применяются по порядку при записи и в обратном порядке при чтении. Пример: [lowercase, encrypt] преобразует строку в нижний регистр, затем шифрует при записи; при чтении — расшифрует без изменений.

  • utc: boolean - Хранить/извлекать даты в часовом поясе UTC вместо локального. Только для типа date. По умолчанию false (локальный пояс для обратной совместимости).

Важно: большинство этих опций специфичны для реляционных СУБД и недоступны в MongoDB.

Наследование сущностей

Дублирование кода можно сократить через наследование сущностей.

Например, у вас есть сущности Photo, Question, Post:

@Entity()
export class Photo {
@PrimaryGeneratedColumn()
id: number

@Column()
title: string

@Column()
description: string

@Column()
size: string
}

@Entity()
export class Question {
@PrimaryGeneratedColumn()
id: number

@Column()
title: string

@Column()
description: string

@Column()
answersCount: number
}

@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number

@Column()
title: string

@Column()
description: string

@Column()
viewCount: number
}

Как видно, все они содержат общие столбцы: id, title, description. Для устранения дублирования можно создать базовый класс Content:

export abstract class Content {
@PrimaryGeneratedColumn()
id: number

@Column()
title: string

@Column()
description: string
}
@Entity()
export class Photo extends Content {
@Column()
size: string
}

@Entity()
export class Question extends Content {
@Column()
answersCount: number
}

@Entity()
export class Post extends Content {
@Column()
viewCount: number
}

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

Иерархические сущности

TypeORM поддерживает паттерны Adjacency list (Список смежности) и Closure table (Таблица замыканий) для хранения древовидных структур.

Adjacency list (Список смежности)

Простейшая модель с самоссылкой. Преимущество - простота реализации, недостаток - невозможность загрузить большое дерево целиком из-за ограничений JOIN. Пример:

import {
Entity,
Column,
PrimaryGeneratedColumn,
ManyToOne,
OneToMany,
} from "typeorm"

@Entity()
export class Category {
@PrimaryGeneratedColumn()
id: number

@Column()
name: string

@Column()
description: string

@ManyToOne((type) => Category, (category) => category.children)
parent: Category

@OneToMany((type) => Category, (category) => category.parent)
children: Category[]
}

Closure table (Таблица замыканий)

Отдельная таблица для хранения отношений родитель-потомок со специальной структурой. Эффективна для чтения и записи. Подробнее в презентации Bill Karwin. Пример:

import {
Entity,
Tree,
Column,
PrimaryGeneratedColumn,
TreeChildren,
TreeParent,
TreeLevelColumn,
} from "typeorm"

@Entity()
@Tree("closure-table")
export class Category {
@PrimaryGeneratedColumn()
id: number

@Column()
name: string

@Column()
description: string

@TreeChildren()
children: Category[]

@TreeParent()
parent: Category

@TreeLevelColumn()
level: number
}