上周帮同事调试一个NestJS项目,他盯着报错信息哀嚎:“TypeORM连不上数据库,文档全是英文术语!” 这场景太熟悉了——三年前我第一次用TypeORM时,光搞懂DataSource
和Entity
的关系就花了整晚。今天分享的极简入门法,专治“文档劝退症”!
一、为什么选TypeORM?新手绕不开的三大优势
零配置玩转多数据库:
它支持MySQL、PostgreSQL甚至MongoDB,切换时只需改
DataSource
的type
参数。我上个月把本地测试库从MySQL换成SQLite,代码一行没改——这对频繁切换环境的开发者太友好了。用TS类型代替SQL语句:
比如定义用户表字段时直接写
@Column({ type: "varchar", length: 200 }) name: string
,TypeORM会自动生成建表语句。再也不用背VARCHAR(255)
这种细节了!关联查询像套娃一样简单:
举个例子:查用户订单时,只要在
User
实体里加@OneToMany(() => Order, order => order.user) orders: Order[]
,调用userRepository.find({ relations: ['orders'] })
就能连带查出所有订单。比手写JOIN
省心十倍。
但别急着欢呼!TypeORM有两个坑新人必踩:
同步模式千万别上线:
synchronize: true
在开发时自动同步表结构很方便,但生产环境可能误删字段。去年我团队就因这个配置丢了客户数据,血泪教训啊!Repository的
save
方法有陷阱:它默认返回完整实体对象,但如果插入时某字段为
null
,数据库默认值会失效!更稳的做法是用insert
+手动赋值。
二、手把手搭建第一个项目(附避坑代码)
Step 1:初始化项目
bash复制npm install typeorm @nestjs/typeorm mysql2 # 用MySQL示例 npx typeorm init --name my-project --database mysql
关键配置:打开data-source.ts
,把synchronize
改成false
!然后手动创建src/entity/User.ts
实体文件。
Step 2:实体定义防坑指南
typescript复制import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column({ default: "匿名用户" }) // 必须显式声明默认值! name: string; // 时间字段强烈推荐用这个 @CreateDateColumn({ precision: 3 }) // 精确到毫秒 createdAt: Date; }
易错点:若漏写@Column
装饰器,该字段会被忽略。我建议安装ESLint插件eslint-plugin-typeorm
自动检查。
Step 3:实战查询技巧
typescript复制// 避免用findOne!它可能返回undefined const user = await userRepository.findOneBy({ id: 1 }); // 分页查询正确姿势 const [users, total] = await userRepository.findAndCount({ skip: (page - 1) * 10, take: 10, order: { createdAt: "DESC" } }); // 关联查询记得加relations const userWithOrders = await userRepository.findOne({ where: { id: 1 }, relations: ["orders"] });
三、高频报错自救方案(亲测有效)
坑①:Cannot find module 'entity/User'
👉 在data-source.ts
的entities
数组里改用[__dirname + "/entity/*.js"]
(编译后是js文件!)
坑②:关联查询结果为空
👉 检查实体类关系装饰器的eager
参数。如果设为true
,关联数据会自动加载,否则需要手动relations
坑③:QueryFailedError: Duplicate entry
👉 用upsert
替代save
!它支持唯一约束冲突时自动更新:
typescript复制await userRepository.upsert( { name: "李雷", email: "lilei@test.com" }, ["email"] // 以email为唯一键 );
TypeORM像一把瑞士军刀——功能多但需要熟悉卡扣位置。如果你卡在某个报错超过半小时,直接去GitHub的Discussion板块搜错误码(别翻Issue!),那里有大量民间高手总结的野路子解法。
(小贴士:实体字段改名时,先用@Column({ name: "old_name" })
兼容旧数据,再逐步迁移)