TypeORM Node ORM
FreeGuideOnline
最新
2026-07-12
bash mkdir typeorm-tutorial cd typeorm-tutorial npm init -y npm install typeorm reflect-metadata @types/node --save npm install typescript ts-node --save-dev
初始化 TypeScript 配置文件:
```bash
npx tsc --init
确保 tsconfig.json 中开启 experimentalDecorators 和 emitDecoratorMetadata:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
安装数据库驱动
npm install sqlite3 --save
配置数据库连接
创建数据源
在项目根目录创建 src/data-source.ts:
import "reflect-metadata"
import { DataSource } from "typeorm"
export const AppDataSource = new DataSource({
type: "sqlite",
database: "database.sqlite",
synchronize: true, // 开发环境可开启,自动同步实体到数据库
logging: false,
entities: [__dirname + "/entity/*.ts"],
migrations: [],
subscribers: [],
})
需要手动修改 ormconfig.json 或使用如上代码形式。之后在应用入口初始化连接:
import { AppDataSource } from "./data-source"
AppDataSource.initialize()
.then(() => console.log("数据库连接成功"))
.catch((error) => console.log(error))
连接选项详解
type:数据库类型host/port:数据库地址和端口(SQLite 不需要)username/password:认证信息database:数据库名或文件路径synchronize:是否自动根据实体创建表结构(生产环境禁用)logging:是否打印 SQLentities:实体类路径
定义实体
实体概念
实体是映射到数据库表的类,使用装饰器声明列和关系。
第一个实体
创建 src/entity/User.ts:
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column()
name: string
@Column()
email: string
}
实体必须由 @Entity() 装饰,至少包含一个主键列。
常用装饰器
| 装饰器 | 作用 |
|---|---|
@Entity() |
标记为数据库表 |
@Column() |
普通列 |
@PrimaryColumn() |
手动赋值主键 |
@PrimaryGeneratedColumn() |
自增主键 |
@CreateDateColumn() |
自动记录创建时间 |
@UpdateDateColumn() |
自动记录更新时间 |
主键与自动生成列
@PrimaryGeneratedColumn("uuid")
id: string
可指定主键生成策略:"increment", "uuid" 等。
枚举与默认值
@Column({
type: "varchar",
default: "active"
})
status: string
@Column({
type: "enum",
enum: ["admin", "user"],
default: "user"
})
role: string
实体的CRUD操作
使用EntityManager
import { AppDataSource } from "./data-source"
import { User } from "./entity/User"
const manager = AppDataSource.manager
// 新增
const user = new User()
user.name = "Alice"
user.email = "alice@example.com"
await manager.save(user)
// 查询
const users = await manager.find(User)
// 更新
user.name = "Alice Updated"
await manager.save(user)
// 删除
await manager.remove(user)
使用Repository
const userRepository = AppDataSource.getRepository(User)
const newUser = userRepository.create({ name: "Bob", email: "bob@test.com" })
await userRepository.save(newUser)
const bob = await userRepository.findOneBy({ name: "Bob" })
if (bob) {
bob.email = "newbob@test.com"
await userRepository.save(bob)
}
await userRepository.delete({ id: 1 })
高级查询
// 使用条件对象
const users = await userRepository.findBy({ role: "admin" })
// 使用find选项
const result = await userRepository.find({
select: ["name", "email"],
where: { name: "Alice" },
order: { id: "DESC" },
skip: 0,
take: 10,
})
实体关系
一对一关系
@Entity()
export class Profile {
@PrimaryGeneratedColumn()
id: number
@Column()
gender: string
@OneToOne(() => User, user => user.profile)
user: User
}
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column()
name: string
@OneToOne(() => Profile, profile => profile.user)
@JoinColumn()
profile: Profile
}
一对多 / 多对一关系
// 多对一(多个文章属于一个用户)
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number
@Column()
title: string
@ManyToOne(() => User, user => user.posts)
user: User
}
// 一对多(一个用户有多篇文章)
@Entity()
export class User {
// ...
@OneToMany(() => Post, post => post.user)
posts: Post[]
}
多对多关系
@Entity()
export class Category {
@PrimaryGeneratedColumn()
id: number
@Column()
name: string
@ManyToMany(() => Question, question => question.categories)
questions: Question[]
}
@Entity()
export class Question {
@PrimaryGeneratedColumn()
id: number
@Column()
text: string
@ManyToMany(() => Category, category => category.questions)
@JoinTable()
categories: Category[]
}
级联操作
在关系装饰器中设置 cascade: true 可以在保存父实体时自动保存关联的实体。
@OneToMany(() => Post, post => post.user, { cascade: true })
posts: Post[]
查询构建器
基本用法
QueryBuilder 允许创建复杂 SQL 查询。
const posts = await AppDataSource.getRepository(Post)
.createQueryBuilder("post")
.leftJoinAndSelect("post.user", "user")
.where("user.name = :name", { name: "Alice" })
.getMany()
复杂查询示例
const users = await AppDataSource
.getRepository(User)
.createQueryBuilder("user")
.select(["user.name", "COUNT(post.id)", "count"])
.leftJoin("user.posts", "post")
.groupBy("user.id")
.having("count > 2")
.getRawMany()
迁移
迁移简介
迁移用于版本化数据库架构变更,方便团队协作。TypeORM 可以自动根据实体变化生成迁移文件。
生成迁移
在 data-source 配置中指定迁移目录:
migrations: ["src/migration/*.ts"]
然后运行命令生成迁移:
npx typeorm migration:generate src/migration/Init -d src/data-source.ts
-d 指定数据源路径,Init 是迁移名称。
运行与回滚迁移
# 运行所有待处理的迁移
npx typeorm migration:run -d src/data-source.ts
# 回滚最近一次迁移
npx typeorm migration:revert -d src/data-source.ts
高级特性
监听器与订阅者
使用 @AfterInsert、@BeforeUpdate 等装饰器添加生命周期钩子。
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column()
name: string
@BeforeInsert()
beforeInsert() {
console.log("即将插入用户:", this.name)
}
}
事务
await AppDataSource.transaction(async (manager) => {
await manager.save(user1)
await manager.save(user2)
// 任何错误都会自动回滚
})
也可使用 QueryRunner 手动管理。
索引与唯一约束
@Entity()
@Index(["name", "email"], { unique: true })
export class User {
@Column({ unique: true })
email: string
}
实战示例:构建博客系统
项目结构
src/
├── entity/
│ ├── User.ts
│ ├── Post.ts
│ └── Comment.ts
├── data-source.ts
└── index.ts
定义三个实体:User、Post、Comment。User 与 Post 是一对多,Post 与 Comment 是一对多。
// User.ts
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column()
username: string
@OneToMany(() => Post, post => post.author)
posts: Post[]
}
// Post.ts
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number
@Column()
title: string
@Column("text")
content: string
@ManyToOne(() => User, user => user.posts)
author: User
@OneToMany(() => Comment, comment => comment.post)
comments: Comment[]
}
// Comment.ts
@Entity()
export class Comment {
@PrimaryGeneratedColumn()
id: number
@Column()
text: string
@ManyToOne(() => Post, post => post.comments)
post: Post
}