One-to-one relations
One-to-one is a relation where A contains only one instance of B, and B contains only one instance of A.
Let's take for example User
and Profile
entities.
User can have only a single profile, and a single profile is owned by only a single user.
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(() => Profile)
@JoinColumn()
profile: Profile
}
Here we added @OneToOne
to the user
and specify the target relation type to be Profile
.
We also added @JoinColumn
which is required and must be set only on one side of the relation.
The side you set @JoinColumn
on, that side's table will contain a "relation id" and foreign keys to target entity table.
This example will produce following tables:
+-------------+--------------+----------------------------+
| profile |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| gender | varchar(255) | |
| photo | varchar(255) | |
+-------------+--------------+----------------------------+
+-------------+--------------+----------------------------+
| user |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(255) | |
| profileId | int(11) | FOREIGN KEY |
+-------------+--------------+----------------------------+
Again, @JoinColumn
must be set only on one side of relation - the side that must have the foreign key in the database table.
Example how to save such a relation:
const profile = new Profile()
profile.gender = "male"
profile.photo = "me.jpg"
await dataSource.manager.save(profile)
const user = new User()
user.name = "Joe Smith"
user.profile = profile
await dataSource.manager.save(user)
With cascades enabled you can save this relation with only one save
call.
To load user with profile inside you must specify relation in FindOptions
:
const users = await dataSource.getRepository(User).find({
relations: {
profile: true,
},
})
Or using QueryBuilder
you can join them:
const users = await dataSource
.getRepository(User)
.createQueryBuilder("user")
.leftJoinAndSelect("user.profile", "profile")
.getMany()
With eager loading enabled on a relation, you don't have to specify relations in the find command as it will ALWAYS be loaded automatically. If you use QueryBuilder eager relations are disabled, you have to use leftJoinAndSelect
to load the relation.
Relations can be uni-directional and bi-directional. Uni-directional are relations with a relation decorator only on one side. Bi-directional are relations with decorators on both sides of a relation.
We just created a uni-directional relation. Let's make it bi-directional:
import { Entity, PrimaryGeneratedColumn, Column, OneToOne } from "typeorm"
import { User } from "./User"
@Entity()
export class Profile {
@PrimaryGeneratedColumn()
id: number
@Column()
gender: string
@Column()
photo: string
@OneToOne(() => User, (user) => user.profile) // specify inverse side as a second parameter
user: User
}
import {
Entity,
PrimaryGeneratedColumn,
Column,
OneToOne,
JoinColumn,
} from "typeorm"
import { Profile } from "./Profile"
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column()
name: string
@OneToOne(() => Profile, (profile) => profile.user) // specify inverse side as a second parameter
@JoinColumn()
profile: Profile
}
We just made our relation bi-directional. Note, inverse relation does not have a @JoinColumn
.
@JoinColumn
must only be on one side of the relation - on the table that will own the foreign key.
Bi-directional relations allow you to join relations from both sides using QueryBuilder
:
const profiles = await dataSource
.getRepository(Profile)
.createQueryBuilder("profile")
.leftJoinAndSelect("profile.user", "user")
.getMany()