Sicht-Entitäten
Diese Seite wurde von PageTurner AI übersetzt (Beta). Nicht offiziell vom Projekt unterstützt. Fehler gefunden? Problem melden →
Was ist eine ViewEntity?
Eine Sicht-Entität ist eine Klasse, die auf eine Datenbank-View abgebildet wird.
Du erstellst eine Sicht-Entität, indem du eine neue Klasse definierst und sie mit @ViewEntity() markierst:
@ViewEntity() akzeptiert folgende Optionen:
-
name- Name der View. Wenn nicht angegeben, wird der View-Name aus dem Klassennamen der Entität generiert. -
database- Datenbankname im ausgewählten DB-Server. -
schema- Name des Schemas. -
expression- View-Definition. Erforderlicher Parameter. -
dependsOn- Liste anderer Views, von denen die aktuelle View abhängt. Wenn deine View in ihrer Definition eine andere View verwendet, kannst du sie hier hinzufügen, damit Migrationen in der richtigen Reihenfolge generiert werden.
expression kann ein String mit korrekt maskierten Spalten und Tabellen sein (abhängig von der verwendeten Datenbank, hier PostgreSQL-Beispiel):
@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"
`
})
oder eine Instanz von 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")
})
Hinweis: Parameter-Binding wird aufgrund von Treibereinschränkungen nicht unterstützt. Verwende stattdessen Literal-Parameter.
@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
})
Jede Sicht-Entität muss in deinen DataSource-Optionen registriert werden:
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],
})
Spalten von Sicht-Entitäten
Um Daten aus der View korrekt auf Entitätsspalten abzubilden, musst du Spalten mit dem @ViewColumn()-Dekorator markieren und diese Spalten als Aliase im SELECT-Statement angeben.
Beispiel mit String-Expression-Definition:
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
}
Beispiel mit 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
}
Optionen für View-Spalten
View-Spaltenoptionen definieren zusätzliche Einstellungen für Sicht-Entitätsspalten, ähnlich wie Spaltenoptionen für reguläre Entitäten.
Du kannst View-Spaltenoptionen in @ViewColumn angeben:
@ViewColumn({
name: "postName",
// ...
})
name: string;
Verfügbare Optionen in ViewColumnOptions:
-
name: string- Spaltenname in der Datenbank-View. -
transformer: { from(value: DatabaseType): EntityType, to(value: EntityType): DatabaseType }- Wandelt Eigenschaften von beliebigemDatabaseType(von der Datenbank unterstützt) inEntityTypeum. Transformer-Arrays werden ebenfalls unterstützt und beim Lesen in umgekehrter Reihenfolge angewendet. Da Datenbank-Views schreibgeschützt sind, wirdtransformer.to(value)niemals verwendet.
Indizes für Materialisierte Views
Für Materialisierte Views in PostgreSQL wird die Index-Erstellung unterstützt.
@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
}
Aktuell ist jedoch unique die einzige unterstützte Index-Option für materialisierte Views. Andere Index-Optionen werden ignoriert.
@Index("name-idx", { unique: true })
@ViewColumn()
name: string
Vollständiges Beispiel
Erstellen wir zwei Entitäten und eine View mit aggregierten Daten aus diesen Entitäten:
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
}
Füllen wir dann die Tabellen mit Daten und fragen alle Daten aus der PostCategory-View ab:
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 })
Das Ergebnis in postCategories ist:
[ PostCategory { id: 1, name: 'About BMW', categoryName: 'Cars' },
PostCategory { id: 2, name: 'About Boeing', categoryName: 'Airplanes' } ]
und in postCategory:
PostCategory { id: 1, name: 'About BMW', categoryName: 'Cars' }