Сущности представлений (View Entities)
Эта страница переведена PageTurner AI (бета). Не одобрена официально проектом. Нашли ошибку? Сообщить о проблеме →
Что такое ViewEntity?
ViewEntity — это класс, который отображается на представление базы данных.
Чтобы создать сущность представления, определите новый класс и пометьте его декоратором @ViewEntity():
@ViewEntity() принимает следующие параметры:
-
name- имя представления. Если не указано, генерируется из имени класса сущности. -
database- имя базы данных на выбранном сервере БД. -
schema- имя схемы. -
expression- определение представления. Обязательный параметр. -
dependsOn- список других представлений, от которых зависит текущее. Если ваше представление использует другое представление в своём определении, можно добавить его здесь для корректной генерации миграций в правильном порядке.
expression может быть строкой с правильно экранированными столбцами и таблицами (для примера — Postgres):
@ViewEntity({
expression: `
SELECT "post"."id" AS "id", "post"."name" AS "name", "category"."name" AS "categoryName"
FROM "post" "post"
LEFT JOIN "category" "category" ON "post"."categoryId" = "category"."id"
`
})
или экземпляром QueryBuilder
@ViewEntity({
expression: (dataSource: DataSource) => dataSource
.createQueryBuilder()
.select("post.id", "id")
.addSelect("post.name", "name")
.addSelect("category.name", "categoryName")
.from(Post, "post")
.leftJoin(Category, "category", "category.id = post.categoryId")
})
Примечание: привязка параметров не поддерживается из-за ограничений драйверов. Используйте литеральные параметры.
@ViewEntity({
expression: (dataSource: DataSource) => dataSource
.createQueryBuilder()
.select("post.id", "id")
.addSelect("post.name", "name")
.addSelect("category.name", "categoryName")
.from(Post, "post")
.leftJoin(Category, "category", "category.id = post.categoryId")
.where("category.name = :name", { name: "Cars" }) // <-- this is wrong
.where("category.name = 'Cars'") // <-- and this is right
})
Каждая сущность представления должна быть зарегистрирована в настройках источника данных:
import { DataSource } from "typeorm"
import { UserView } from "./entities/UserView"
const dataSource = new DataSource({
type: "mysql",
host: "localhost",
port: 3306,
username: "test",
password: "test",
database: "test",
entities: [UserView],
})
Столбцы сущности представления
Для корректного отображения данных из представления в столбцы сущности пометьте столбцы декоратором @ViewColumn()
и укажите их как алиасы в SELECT-выражении.
пример с определением в виде строки:
import { ViewEntity, ViewColumn } from "typeorm"
@ViewEntity({
expression: `
SELECT "post"."id" AS "id", "post"."name" AS "name", "category"."name" AS "categoryName"
FROM "post" "post"
LEFT JOIN "category" "category" ON "post"."categoryId" = "category"."id"
`,
})
export class PostCategory {
@ViewColumn()
id: number
@ViewColumn()
name: string
@ViewColumn()
categoryName: string
}
пример с использованием QueryBuilder:
import { ViewEntity, ViewColumn } from "typeorm"
@ViewEntity({
expression: (dataSource: DataSource) =>
dataSource
.createQueryBuilder()
.select("post.id", "id")
.addSelect("post.name", "name")
.addSelect("category.name", "categoryName")
.from(Post, "post")
.leftJoin(Category, "category", "category.id = post.categoryId"),
})
export class PostCategory {
@ViewColumn()
id: number
@ViewColumn()
name: string
@ViewColumn()
categoryName: string
}
Параметры столбцов представления
Параметры столбцов представления определяют дополнительные настройки, аналогично параметрам столбцов для обычных сущностей.
Параметры можно указать в @ViewColumn:
@ViewColumn({
name: "postName",
// ...
})
name: string;
Доступные параметры в ViewColumnOptions:
-
name: string- имя столбца в представлении базы данных. -
transformer: { from(value: DatabaseType): EntityType, to(value: EntityType): DatabaseType }- используется для преобразования свойств произвольного типаDatabaseType(поддерживаемого БД) в типEntityType. Поддерживаются массивы преобразователей, применяемые в обратном порядке при чтении. Поскольку представления доступны только для чтения,transformer.to(value)никогда не используется.
Индексы для материализованных представлений
Поддерживается создание индексов для материализованных представлений в PostgreSQL.
@ViewEntity({
materialized: true,
expression: (dataSource: DataSource) =>
dataSource
.createQueryBuilder()
.select("post.id", "id")
.addSelect("post.name", "name")
.addSelect("category.name", "categoryName")
.from(Post, "post")
.leftJoin(Category, "category", "category.id = post.categoryId"),
})
export class PostCategory {
@ViewColumn()
id: number
@Index()
@ViewColumn()
name: string
@Index("catname-idx")
@ViewColumn()
categoryName: string
}
Однако в настоящее время для индексов материализованных представлений поддерживается только опция unique. Остальные параметры индексов игнорируются.
@Index("name-idx", { unique: true })
@ViewColumn()
name: string
Полный пример
Создадим две сущности и представление с агрегированными данными из них:
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"
@Entity()
export class Category {
@PrimaryGeneratedColumn()
id: number
@Column()
name: string
}
import {
Entity,
PrimaryGeneratedColumn,
Column,
ManyToOne,
JoinColumn,
} from "typeorm"
import { Category } from "./Category"
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number
@Column()
name: string
@Column()
categoryId: number
@ManyToOne(() => Category)
@JoinColumn({ name: "categoryId" })
category: Category
}
import { ViewEntity, ViewColumn, DataSource } from "typeorm"
@ViewEntity({
expression: (dataSource: DataSource) =>
dataSource
.createQueryBuilder()
.select("post.id", "id")
.addSelect("post.name", "name")
.addSelect("category.name", "categoryName")
.from(Post, "post")
.leftJoin(Category, "category", "category.id = post.categoryId"),
})
export class PostCategory {
@ViewColumn()
id: number
@ViewColumn()
name: string
@ViewColumn()
categoryName: string
}
заполним таблицы данными и запросим все данные из представления PostCategory:
import { Category } from "./entities/Category"
import { Post } from "./entities/Post"
import { PostCategory } from "./entities/PostCategory"
const category1 = new Category()
category1.name = "Cars"
await dataSource.manager.save(category1)
const category2 = new Category()
category2.name = "Airplanes"
await dataSource.manager.save(category2)
const post1 = new Post()
post1.name = "About BMW"
post1.categoryId = category1.id
await dataSource.manager.save(post1)
const post2 = new Post()
post2.name = "About Boeing"
post2.categoryId = category2.id
await dataSource.manager.save(post2)
const postCategories = await dataSource.manager.find(PostCategory)
const postCategory = await dataSource.manager.findOneBy(PostCategory, { id: 1 })
результат в postCategories будет:
[ PostCategory { id: 1, name: 'About BMW', categoryName: 'Cars' },
PostCategory { id: 2, name: 'About Boeing', categoryName: 'Airplanes' } ]
а в postCategory:
PostCategory { id: 1, name: 'About BMW', categoryName: 'Cars' }