FAQ sulle Relazioni
Questa pagina è stata tradotta da PageTurner AI (beta). Non ufficialmente approvata dal progetto. Hai trovato un errore? Segnala problema →
Come creare una relazione autoreferenziale?
Le relazioni autoreferenziali sono relazioni che puntano a se stesse. Sono utili quando si memorizzano entità in strutture ad albero. Inoltre, il pattern "adjacency list" viene implementato usando relazioni autoreferenziate. Ad esempio, supponi di voler creare un albero di categorie nella tua applicazione. Le categorie possono contenere altre categorie, che a loro volta possono annidarne altre, e così via. Qui le relazioni autoreferenziali diventano pratiche. Fondamentalmente sono relazioni normali che puntano all'entità stessa. Esempio:
import {
Entity,
PrimaryGeneratedColumn,
Column,
ManyToOne,
OneToMany,
} from "typeorm"
@Entity()
export class Category {
@PrimaryGeneratedColumn()
id: number
@Column()
title: string
@Column()
text: string
@ManyToOne((type) => Category, (category) => category.childCategories)
parentCategory: Category
@OneToMany((type) => Category, (category) => category.parentCategory)
childCategories: Category[]
}
Come usare l'ID di relazione senza caricare la relazione?
A volte potresti volere nel tuo oggetto l'ID dell'oggetto correlato senza caricarlo. Per esempio:
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"
@Entity()
export class Profile {
@PrimaryGeneratedColumn()
id: number
@Column()
gender: string
@Column()
photo: string
}
import {
Entity,
PrimaryGeneratedColumn,
Column,
OneToOne,
JoinColumn,
} from "typeorm"
import { Profile } from "./Profile"
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column()
name: string
@OneToOne((type) => Profile)
@JoinColumn()
profile: Profile
}
Quando carichi un utente senza unire profile, non avrai alcuna informazione sul profilo nell'oggetto utente,
nemmeno l'ID del profilo:
User {
id: 1,
name: "Umed"
}
Ma a volte vuoi conoscere l'ID del profilo di questo utente senza caricare l'intero profilo.
Per farlo, aggiungi semplicemente un'altra proprietà alla tua entità con @Column
chiamata esattamente come la colonna creata dalla tua relazione. Esempio:
import {
Entity,
PrimaryGeneratedColumn,
Column,
OneToOne,
JoinColumn,
} from "typeorm"
import { Profile } from "./Profile"
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column()
name: string
@Column({ nullable: true })
profileId: number
@OneToOne((type) => Profile)
@JoinColumn()
profile: Profile
}
Tutto qui. La prossima volta che caricherai un oggetto utente conterrà l'ID del profilo:
User {
id: 1,
name: "Umed",
profileId: 1
}
Come caricare le relazioni nelle entità?
Il modo più semplice è usare l'opzione relations in FindOptions:
const users = await dataSource.getRepository(User).find({
relations: {
profile: true,
photos: true,
videos: true,
},
})
Un'alternativa più flessibile è usare QueryBuilder:
const user = await dataSource
.getRepository(User)
.createQueryBuilder("user")
.leftJoinAndSelect("user.profile", "profile")
.leftJoinAndSelect("user.photos", "photo")
.leftJoinAndSelect("user.videos", "video")
.getMany()
Con QueryBuilder puoi usare innerJoinAndSelect invece di leftJoinAndSelect
(per la differenza tra LEFT JOIN e INNER JOIN consulta la documentazione SQL),
puoi unire dati di relazione con condizioni, effettuare ordinamenti, ecc.
Scopri di più su QueryBuilder.
Evitare gli inizializzatori delle proprietà di relazione
A volte può essere utile inizializzare le proprietà di relazione, ad esempio:
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((type) => Category, (category) => category.questions)
@JoinTable()
categories: Category[] = [] // see = [] initialization here
}
Tuttavia, nelle entità TypeORM questo può causare problemi. Per capire il problema, proviamo prima a caricare un'entità Question SENZA inizializzatore. Quando carichi una domanda, otterrai un oggetto così:
Question {
id: 1,
title: "Question about ..."
}
Ora quando salvi questo oggetto, categories al suo interno non verrà toccato - perché non è impostato.
Ma se hai un inizializzatore, l'oggetto caricato apparirà così:
Question {
id: 1,
title: "Question about ...",
categories: []
}
Quando salvi l'oggetto, TypeORM controllerà se ci sono categorie nel database collegate alla domanda -
e le scollegherà tutte. Perché? Perché una relazione uguale a [] o con elementi interni verrà considerata
come se qualcosa fosse stato rimosso, non essendoci altro modo per verificare se un oggetto è stato rimosso dall'entità.
Quindi, salvare un oggetto così causerà problemi - rimuoverà tutte le categorie precedentemente impostate.
Come evitare questo comportamento? Semplicemente non inizializzare gli array nelle tue entità. La stessa regola vale per il costruttore - non inizializzarlo nemmeno lì.
Evitare la creazione di vincoli di chiave esterna
A volte per ragioni prestazionali potresti volere una relazione tra entità senza vincolo di chiave esterna.
Puoi definire se creare il vincolo con l'opzione createForeignKeyConstraints (default: true).
import { Entity, PrimaryColumn, Column, ManyToOne } from "typeorm"
import { Person } from "./Person"
@Entity()
export class ActionLog {
@PrimaryColumn()
id: number
@Column()
date: Date
@Column()
action: string
@ManyToOne((type) => Person, {
createForeignKeyConstraints: false,
})
person: Person
}
Evitare errori di importazione circolare
Ecco un esempio per definire le tue entità senza causare errori in alcuni ambienti. In questo scenario, Action.ts e Person.ts si importano reciprocamente per una relazione many-to-many. Usiamo import type per utilizzare le informazioni di tipo senza generare codice JavaScript.
import { Entity, PrimaryColumn, Column, ManytoMany } from "typeorm"
import type { Person } from "./Person"
@Entity()
export class ActionLog {
@PrimaryColumn()
id: number
@Column()
date: Date
@Column()
action: string
@ManyToMany("Person", (person: Person) => person.id)
person: Person
}
import { Entity, PrimaryColumn, ManytoMany } from "typeorm"
import type { ActionLog } from "./Action"
@Entity()
export class Person {
@PrimaryColumn()
id: number
@ManyToMany("ActionLog", (actionLog: ActionLog) => actionLog.id)
log: ActionLog
}