Saltar al contenido principal

Relaciones Muchos-a-uno / Uno-a-muchos

Traducción Beta No Oficial

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

Muchos-a-uno / uno-a-muchos es una relación donde A contiene múltiples instancias de B, pero B contiene solo una instancia de A. Tomemos como ejemplo las entidades User y Photo. Un usuario puede tener múltiples fotos, pero cada foto pertenece a un único usuario.

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

Aquí agregamos @OneToMany a la propiedad photos y especificamos que el tipo de relación objetivo es Photo. Puedes omitir @JoinColumn en una relación @ManyToOne / @OneToMany. @OneToMany no puede existir sin @ManyToOne. Si deseas usar @OneToMany, @ManyToOne es obligatorio. Sin embargo, lo inverso no es obligatorio: si solo te importa la relación @ManyToOne, puedes definirla sin tener @OneToMany en la entidad relacionada. Donde establezcas @ManyToOne, su entidad relacionada tendrá "relation id" y una clave foránea.

Este ejemplo generará las siguientes tablas:

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

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

Ejemplo de cómo guardar dicha relación:

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)

o alternativamente puedes hacer:

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 las cascadas habilitadas, puedes guardar esta relación con solo una llamada a save.

Para cargar un usuario con sus fotos, debes especificar la relación en 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,
},
})

O usando QueryBuilder puedes unirlas:

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 la carga ansiosa (eager loading) habilitada en una relación, no necesitas especificar relaciones en el comando find ya que SIEMPRE se cargará automáticamente. Si usas QueryBuilder, las relaciones con carga ansiosa están deshabilitadas, por lo que debes usar leftJoinAndSelect para cargar la relación.