当前位置: 首页 > news >正文

TypeORM 入门教程之 `@OneToOne` 关系详解

@OneToOne 是 TypeORM 中用于定义实体间一对一关系的装饰器,适用于两个实体间存在严格唯一对应关系的场景(如用户与身份证、订单与发票)。以下是其核心用法和关键配置:

基础用法

1. 单向一对一关系

场景:仅在一个实体中定义对另一个实体的引用。

// User.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm';
import { Profile } from './Profile.entity';@Entity()
export class User {@PrimaryGeneratedColumn()id: number;@Column()name: string;// 单向关联(User 知道 Profile,但 Profile 不知道 User)@OneToOne(() => Profile)profile: Profile;
}// Profile.entity.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';@Entity()
export class Profile {@PrimaryGeneratedColumn()id: number;@Column()bio: string;
}

数据库结果

  • User 表会新增一个外键列(如 profileId),指向 Profile 表的主键。

2. 双向一对一关系

场景:两个实体相互引用对方,需通过 @JoinColumn 指定关系所有者。

// User.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } from 'typeorm';
import { Profile } from './Profile.entity';@Entity()
export class User {@PrimaryGeneratedColumn()id: number;@Column()name: string;// 双向关联(User 是关系所有者)@OneToOne(() => Profile, (profile) => profile.user)@JoinColumn() // 必须指定,否则会报错profile: Profile;
}// Profile.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm';
import { User } from './User.entity';@Entity()
export class Profile {@PrimaryGeneratedColumn()id: number;@Column()bio: string;// 反向引用(非必须,但推荐以支持双向查询)@OneToOne(() => User, (user) => user.profile)user: User;
}

数据库结果

  • 外键列仅在关系所有者(User)的表中生成(如 profileId)。

关键配置

1. @JoinColumn 详解

  • 作用:指定关系所有者,并定义外键列名。
  • 必填性:在双向关系中,必须在关系所有者一侧添加。
  • 自定义外键名
    @OneToOne(() => Profile, (profile) => profile.user)
    @JoinColumn({ name: 'custom_profile_id' }) // 自定义外键列名
    profile: Profile;
    

2. 级联操作(Cascade)

通过 cascade 选项自动管理关联实体的生命周期:

@OneToOne(() => Profile, (profile) => profile.user, {cascade: true, // 自动保存/更新/删除关联实体onDelete: 'CASCADE', // 数据库级联删除(可选)
})
@JoinColumn()
profile: Profile;

3. 通过主键关联(类似 JPA @MapsId

场景:当关联实体的外键与主键相同时(如 Client.id = User.id)。

// Client.entity.ts
import { Entity, PrimaryColumn, OneToOne, JoinColumn } from 'typeorm';
import { User } from './User.entity';@Entity()
export class Client {@PrimaryColumn() // 必须使用 @PrimaryColumn 而非 @PrimaryGeneratedColumnid: number;@OneToOne(() => User, { primary: true }) // primary: true 表示通过主键关联@JoinColumn({ name: 'id' }) // 外键列名与主键相同user: User;
}

查询操作

1. 加载关联数据

  • 隐式加载(需配置 eager: true):
    @OneToOne(() => Profile, (profile) => profile.user, { eager: true })
    profile: Profile;
    
  • 显式加载(推荐):
    const user = await userRepository.find({relations: ['profile'], // 加载关联的 profile
    });
    

2. 查询构建器(QueryBuilder)

const user = await userRepository.createQueryBuilder('user').leftJoinAndSelect('user.profile', 'profile') // 左连接并选择 profile.where('user.id = :id', { id: 1 }).getOne();

完整示例

// User.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } from 'typeorm';
import { Profile } from './Profile.entity';@Entity()
export class User {@PrimaryGeneratedColumn()id: number;@Column()name: string;@OneToOne(() => Profile, (profile) => profile.user, {cascade: true,onDelete: 'CASCADE',})@JoinColumn()profile: Profile;
}// Profile.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm';
import { User } from './User.entity';@Entity()
export class Profile {@PrimaryGeneratedColumn()id: number;@Column()bio: string;@OneToOne(() => User, (user) => user.profile)user: User;
}// 使用示例
async function createUserWithProfile() {const profile = new Profile();profile.bio = 'TypeORM Expert';const user = new User();user.name = 'Alice';user.profile = profile; // 自动级联保存await userRepository.save(user);
}async function getUserWithProfile(id: number) {const user = await userRepository.findOne({where: { id },relations: ['profile'], // 加载关联数据});console.log(user?.profile?.bio); // 输出: TypeORM Expert
}

常见问题

  1. 外键列未生成

    • 检查是否在关系所有者一侧添加了 @JoinColumn
    • 确保实体已正确同步到数据库(运行迁移或重新启动应用)。
  2. 循环依赖

    • 避免在双向关系的装饰器回调中直接引用对方实体(如 @OneToOne(() => Profile, (profile) => profile.user) 是安全的,但循环导入模块会导致问题)。
  3. 性能优化

    • 对频繁查询的关联数据使用 eager: true@RelationId 装饰器。
    • 对复杂查询使用 QueryBuilder 以减少 N+1 问题。

文章转载自:

http://wXngV5ST.xzxcz.cn
http://nHyGVSfX.xzxcz.cn
http://1WfZ4bfh.xzxcz.cn
http://1NZ34wje.xzxcz.cn
http://RVrIwAzM.xzxcz.cn
http://97eXqtZE.xzxcz.cn
http://wkFr86R0.xzxcz.cn
http://6KP6bVmh.xzxcz.cn
http://nGEyBlVD.xzxcz.cn
http://5wsvAqla.xzxcz.cn
http://649inQsU.xzxcz.cn
http://MRzqcfW7.xzxcz.cn
http://Qvhhv1ao.xzxcz.cn
http://dkJTqqub.xzxcz.cn
http://uGgklhZU.xzxcz.cn
http://jBAQ0Idj.xzxcz.cn
http://4ICSRdN5.xzxcz.cn
http://fWaZOM8m.xzxcz.cn
http://6wJZfOZG.xzxcz.cn
http://M9Nc9yls.xzxcz.cn
http://SBiShdeQ.xzxcz.cn
http://bPSjcEbI.xzxcz.cn
http://rltoYMwt.xzxcz.cn
http://e83nZxBO.xzxcz.cn
http://GebfNRSO.xzxcz.cn
http://zBklMkq3.xzxcz.cn
http://RojsAGAM.xzxcz.cn
http://gaQMyEpg.xzxcz.cn
http://F7N3MLrt.xzxcz.cn
http://yRboaRSM.xzxcz.cn
http://www.dtcms.com/a/372207.html

相关文章:

  • 嵌入式解谜日志之数据结构—基本概念
  • make_shared的使用
  • 《九江棒球》未来十年棒垒球发展规划·棒球1号位
  • agentscope1.0安装与测试
  • Shell 脚本自动安装 Nginx
  • 《探索C++11:现代语法的内存管理优化“性能指针”(下篇)》
  • Basic Pentesting: 1靶场渗透
  • NAS自建笔记服务leanote2
  • 对比Java学习Go——程序结构与变量
  • 【JavaWeb】一个简单的Web浏览服务程序
  • [C/C++学习] 7.“旋转蛇“视觉图形生成
  • webhook(Web 钩子)是什么?
  • 《2025年AI产业发展十大趋势报告》四十三
  • java面试小册(1)
  • NW506NW507美光固态闪存NW525NW539
  • [Maven 基础课程]再看下第一个 Maven 项目
  • Keil快捷键代码补全
  • 2024理想算法岗笔试笔记
  • Java面试-线程安全篇
  • 线程池深度解析:ThreadPoolExecutor底层实现与CompletableFuture异步编程实战
  • 计算机网络学习(七、网络安全)
  • 蓝奏云官方版不好用?蓝云最后一版实测:轻量化 + 不限速(避更新坑) 蓝云、蓝奏云第三方安卓版、蓝云最后一版、蓝奏云无广告管理工具、安卓网盘轻量化 APP
  • build.gradle里面dependencies compile和api的区别
  • C++20格式化字符串:std::format的使用与实践
  • UART 使用教程
  • cuda中线程id的计算方式(简单)
  • Archon02-代码解析
  • # 图片格式转换工具:重新定义您的图片处理体验
  • 【Python】S1 基础篇 P2 列表详解:基础操作
  • 液压伺服千斤顶系统设计cad+设计说明书