Vai al contenuto principale

Relazioni Many-to-one / one-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 →

Una relazione many-to-one / one-to-many si verifica quando A contiene più istanze di B, ma B contiene una sola istanza di A.
Prendiamo come esempio le entità User e Photo.
Un utente può avere più foto, ma ogni foto appartiene a un solo utente.

import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm"
import { User } from "./User"

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

@Column()
url: string

@ManyToOne(() => User, (user) => user.photos)
user: User
}
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm"
import { Photo } from "./Photo"

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

@Column()
name: string

@OneToMany(() => Photo, (photo) => photo.user)
photos: Photo[]
}

Qui abbiamo aggiunto @OneToMany alla proprietà photos specificando il tipo di relazione target come Photo.
Puoi omettere @JoinColumn nelle relazioni @ManyToOne / @OneToMany.
@OneToMany non può esistere senza @ManyToOne.
Se vuoi usare @OneToMany, @ManyToOne è obbligatorio. Tuttavia il contrario non è necessario: se ti interessa solo la relazione @ManyToOne, puoi definirla senza avere @OneToMany sull'entità correlata.
Dove imposti @ManyToOne, l'entità correlata avrà un "relation id" e una foreign key.

Questo esempio produrrà le seguenti tabelle:

+-------------+--------------+----------------------------+
| photo |
+-------------+--------------+----------------------------+
| id | int | PRIMARY KEY AUTO_INCREMENT |
| url | varchar(255) | |
| userId | int | FOREIGN KEY |
+-------------+--------------+----------------------------+

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

Esempio di come salvare questa relazione:

const photo1 = new Photo()
photo1.url = "me.jpg"
await dataSource.manager.save(photo1)

const photo2 = new Photo()
photo2.url = "me-and-bears.jpg"
await dataSource.manager.save(photo2)

const user = new User()
user.name = "John"
user.photos = [photo1, photo2]
await dataSource.manager.save(user)

oppure in alternativa puoi fare:

const user = new User()
user.name = "Leo"
await dataSource.manager.save(user)

const photo1 = new Photo()
photo1.url = "me.jpg"
photo1.user = user
await dataSource.manager.save(photo1)

const photo2 = new Photo()
photo2.url = "me-and-bears.jpg"
photo2.user = user
await dataSource.manager.save(photo2)

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

Per caricare un utente con le foto interne, devi specificare la relazione in FindOptions:

const userRepository = dataSource.getRepository(User)
const users = await userRepository.find({
relations: {
photos: true,
},
})

// or from inverse side

const photoRepository = dataSource.getRepository(Photo)
const photos = await photoRepository.find({
relations: {
user: true,
},
})

Oppure usando QueryBuilder puoi effettuare un join:

const users = await dataSource
.getRepository(User)
.createQueryBuilder("user")
.leftJoinAndSelect("user.photos", "photo")
.getMany()

// or from inverse side

const photos = await dataSource
.getRepository(Photo)
.createQueryBuilder("photo")
.leftJoinAndSelect("photo.user", "user")
.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: devi usare leftJoinAndSelect per caricare la relazione.