Zum Hauptinhalt springen

Many-to-many-Beziehungen

Inoffizielle Beta-Übersetzung

Diese Seite wurde von PageTurner AI übersetzt (Beta). Nicht offiziell vom Projekt unterstützt. Fehler gefunden? Problem melden →

Was sind Many-to-many-Beziehungen?

Eine Many-to-many-Beziehung liegt vor, wenn A mehrere Instanzen von B enthält und B mehrere Instanzen von A. Betrachten wir beispielsweise die Entitäten Question und Category. Eine Frage kann mehrere Kategorien haben, und jede Kategorie kann mehrere Fragen enthalten.

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

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

@Column()
name: string
}
import {
Entity,
PrimaryGeneratedColumn,
Column,
ManyToMany,
JoinTable,
} from "typeorm"
import { Category } from "./Category"

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

@Column()
title: string

@Column()
text: string

@ManyToMany(() => Category)
@JoinTable()
categories: Category[]
}

@JoinTable() ist für @ManyToMany-Beziehungen erforderlich. Sie müssen @JoinTable auf einer Seite (der besitzenden Seite) der Beziehung setzen.

Dieses Beispiel erzeugt folgende Tabellen:

+-------------+--------------+----------------------------+
| category |
+-------------+--------------+----------------------------+
| id | int | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(255) | |
+-------------+--------------+----------------------------+

+-------------+--------------+----------------------------+
| question |
+-------------+--------------+----------------------------+
| id | int | PRIMARY KEY AUTO_INCREMENT |
| title | varchar(255) | |
| text | varchar(255) | |
+-------------+--------------+----------------------------+

+-------------+--------------+----------------------------+
| question_categories_category |
+-------------+--------------+----------------------------+
| questionId | int | PRIMARY KEY FOREIGN KEY |
| categoryId | int | PRIMARY KEY FOREIGN KEY |
+-------------+--------------+----------------------------+

Speichern von Many-to-many-Beziehungen

Wenn Kaskaden aktiviert sind, können Sie diese Beziehung mit nur einem save-Aufruf speichern.

const category1 = new Category()
category1.name = "animals"
await dataSource.manager.save(category1)

const category2 = new Category()
category2.name = "zoo"
await dataSource.manager.save(category2)

const question = new Question()
question.title = "dogs"
question.text = "who let the dogs out?"
question.categories = [category1, category2]
await dataSource.manager.save(question)

Löschen von Many-to-many-Beziehungen

Wenn Kaskaden aktiviert sind, können Sie diese Beziehung mit nur einem save-Aufruf löschen.

Um eine Many-to-many-Beziehung zwischen zwei Datensätzen zu entfernen, nehmen Sie sie aus dem entsprechenden Feld heraus und speichern Sie den Datensatz.

const question = await dataSource.getRepository(Question).findOne({
relations: {
categories: true,
},
where: { id: 1 },
})
question.categories = question.categories.filter((category) => {
return category.id !== categoryToRemove.id
})
await dataSource.manager.save(question)

Dadurch wird nur der Eintrag in der Join-Tabelle entfernt. Die Datensätze question und categoryToRemove bleiben bestehen.

Soft Delete einer Beziehung mit Kaskade

Dieses Beispiel zeigt das Verhalten beim kaskadierenden Soft Delete:

const category1 = new Category()
category1.name = "animals"

const category2 = new Category()
category2.name = "zoo"

const question = new Question()
question.categories = [category1, category2]
const newQuestion = await dataSource.manager.save(question)

await dataSource.manager.softRemove(newQuestion)

Hier haben wir save oder softRemove für category1 und category2 nicht aufgerufen, aber sie werden automatisch gespeichert und soft-gelöscht, wenn die Kaskadenoption für Beziehungen wie folgt aktiviert ist:

import {
Entity,
PrimaryGeneratedColumn,
Column,
ManyToMany,
JoinTable,
} from "typeorm"
import { Category } from "./Category"

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

@ManyToMany(() => Category, (category) => category.questions, {
cascade: true,
})
@JoinTable()
categories: Category[]
}

Laden von Many-to-many-Beziehungen

Um Fragen mit ihren Kategorien zu laden, müssen Sie die Beziehung in FindOptions angeben:

const questionRepository = dataSource.getRepository(Question)
const questions = await questionRepository.find({
relations: {
categories: true,
},
})

Oder mit QueryBuilder können Sie sie verknüpfen:

const questions = await dataSource
.getRepository(Question)
.createQueryBuilder("question")
.leftJoinAndSelect("question.categories", "category")
.getMany()

Wenn Eager Loading für eine Beziehung aktiviert ist, müssen Sie sie nicht in find-Befehlen angeben – sie wird IMMER automatisch geladen. Bei QueryBuilder ist Eager Loading deaktiviert; Sie müssen leftJoinAndSelect verwenden.

Bidirektionale Beziehungen

Beziehungen können unidirektional oder bidirektional sein. Unidirektionale Beziehungen haben einen Relationsdekorator nur auf einer Seite. Bidirektionale Beziehungen haben Dekoratoren auf beiden Seiten.

Wir haben bisher eine unidirektionale Beziehung. Machen wir sie bidirektional:

import { Entity, PrimaryGeneratedColumn, Column, ManyToMany } from "typeorm"
import { Question } from "./Question"

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

@Column()
name: string

@ManyToMany(() => Question, (question) => question.categories)
questions: Question[]
}
import {
Entity,
PrimaryGeneratedColumn,
Column,
ManyToMany,
JoinTable,
} from "typeorm"
import { Category } from "./Category"

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

@Column()
title: string

@Column()
text: string

@ManyToMany(() => Category, (category) => category.questions)
@JoinTable()
categories: Category[]
}

Jetzt ist unsere Beziehung bidirektional. Beachten Sie: Die inverse Beziehung hat kein @JoinTable. @JoinTable darf nur auf einer Seite der Beziehung stehen.

Bidirektionale Beziehungen ermöglichen das Verknüpfen von beiden Seiten mit QueryBuilder:

const categoriesWithQuestions = await dataSource
.getRepository(Category)
.createQueryBuilder("category")
.leftJoinAndSelect("category.questions", "question")
.getMany()

Many-to-many-Beziehungen mit benutzerdefinierten Eigenschaften

Wenn Sie zusätzliche Eigenschaften in Ihrer Many-to-many-Beziehung benötigen, müssen Sie eine eigene Entität erstellen. Beispiel: Sollen Question und Category eine Many-to-many-Beziehung mit zusätzlicher order-Spalte haben, erstellen Sie eine Entität QuestionToCategory mit zwei ManyToOne-Beziehungen (in beide Richtungen) und benutzerdefinierten Spalten:

import { Entity, Column, ManyToOne, PrimaryGeneratedColumn } from "typeorm"
import { Question } from "./question"
import { Category } from "./category"

@Entity()
export class QuestionToCategory {
@PrimaryGeneratedColumn()
public questionToCategoryId: number

@Column()
public questionId: number

@Column()
public categoryId: number

@Column()
public order: number

@ManyToOne(() => Question, (question) => question.questionToCategories)
public question: Question

@ManyToOne(() => Category, (category) => category.questionToCategories)
public category: Category
}

Zusätzlich müssen Sie Question und Category jeweils eine Beziehung wie folgt hinzufügen:

// category.ts
...
@OneToMany(() => QuestionToCategory, questionToCategory => questionToCategory.category)
public questionToCategories: QuestionToCategory[];

// question.ts
...
@OneToMany(() => QuestionToCategory, questionToCategory => questionToCategory.question)
public questionToCategories: QuestionToCategory[];