Vai al contenuto principale

Relazioni Many-to-Many

Traduzione Beta Non Ufficiale

Questa pagina è stata tradotta da PageTurner AI (beta). Non ufficialmente approvata dal progetto. Hai trovato un errore? Segnala problema →

Cosa sono le relazioni many-to-many?

Una relazione many-to-many si verifica quando A contiene multiple istanze di B, e B contiene multiple istanze di A.
Prendiamo come esempio le entità Question e Category.
Una domanda può appartenere a più categorie, e ogni categoria può contenere più domande.

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() è obbligatorio per le relazioni @ManyToMany.
Devi posizionare @JoinTable sul lato proprietario (owning side) della relazione.

Questo esempio produrrà le seguenti tabelle:

+-------------+--------------+----------------------------+
| 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 |
+-------------+--------------+----------------------------+

Salvataggio di relazioni many-to-many

Con le cascate abilitate, puoi salvare questa relazione con una sola chiamata save.

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)

Eliminazione di relazioni many-to-many

Con le cascate abilitate, puoi eliminare questa relazione con una sola chiamata save.

Per eliminare una relazione many-to-many tra due record, rimuovila dal campo corrispondente e salva il record.

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)

Questo rimuoverà solo il record nella tabella di join. I record question e categoryToRemove continueranno a esistere.

Soft Delete di una relazione con cascade

Questo esempio mostra il comportamento del soft delete con cascata:

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)

In questo esempio non abbiamo chiamato save o softRemove per category1 e category2, ma verranno salvate e soft-eliminate automaticamente quando l'opzione di cascade è impostata a true così:

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[]
}

Caricamento di relazioni many-to-many

Per caricare le domande con le relative categorie, devi specificare la relazione nelle FindOptions:

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

Oppure usando QueryBuilder puoi effettuare un join:

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

Con l'eager loading abilitato su una relazione, non devi specificare le relazioni nel comando find perché verranno SEMPRE caricate automaticamente. Se usi QueryBuilder, le relazioni eager sono disabilitate e devi usare leftJoinAndSelect per caricare la relazione.

Relazioni bidirezionali

Le relazioni possono essere unidirezionali o bidirezionali.
Le relazioni unidirezionali hanno un decoratore di relazione solo su un lato.
Le relazioni bidirezionali hanno decoratori su entrambi i lati della relazione.

Abbiamo appena creato una relazione unidirezionale. Rendiamola bidirezionale:

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[]
}

Abbiamo reso la relazione bidirezionale. Nota che la relazione inversa non ha @JoinTable.
@JoinTable deve essere presente solo su un lato della relazione.

Le relazioni bidirezionali ti permettono di unire le relazioni da entrambi i lati usando QueryBuilder:

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

Relazioni many-to-many con proprietà personalizzate

Se hai bisogno di proprietà aggiuntive nella tua relazione many-to-many, devi creare manualmente una nuova entità.
Ad esempio, se vuoi che le entità Question e Category abbiano una relazione many-to-many con una colonna aggiuntiva order, devi creare un'entità QuestionToCategory con due relazioni ManyToOne rivolte in entrambe le direzioni e con colonne personalizzate:

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
}

Inoltre dovrai aggiungere una relazione come la seguente a Question e Category:

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

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