eagleye

TypeORM 企业级应用实用教程

TypeORM 企业级应用实用教程

一、概述

TypeORM 是 Node.js 生态中广泛使用的ORM(对象关系映射)框架,支持 TypeScript 全类型安全,提供丰富的数据库交互能力。其核心优势在于平衡开发效率与性能,支持多种数据库(MySQL、PostgreSQL、MongoDB 等),并与 Nest.js、Midway 等企业级框架深度集成,是构建复杂业务系统的理想选择。

二、核心概念

2.1 实体(Entity)

定义:映射数据库表的类,通过装饰器声明表结构与字段属性。

示例

// entity/User.ts

import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';

@Entity() // 默认映射表名:user(可通过 @Entity('custom_table_name') 自定义)

export class User {

@PrimaryGeneratedColumn() // 自增主键(INT 类型)

id: number;

@Column({ length: 100 }) // VARCHAR(100),非空

name: string;

@Column({ unique: true }) // 唯一约束(避免重复邮箱)

email: string;

@Column({ select: false }) // 查询时默认不返回(如密码等敏感字段)

password: string;

@Column({ default: false }) // 默认值

isActive: boolean;

@CreateDateColumn() // 自动记录创建时间(DATETIME)

createTime: Date;

@UpdateDateColumn() // 自动记录更新时间(DATETIME)

updateTime: Date;

}

2.2 数据源(DataSource)

定义:数据库连接的核心配置,封装连接参数、实体映射、迁移等配置。

作用:初始化数据库连接,是与数据库交互的入口。

示例

// data-source.ts

import { DataSource } from 'typeorm';

import { User } from './entity/User';

export const AppDataSource = new DataSource({

type: 'mysql', // 数据库类型(mysql/postgres/sqlite等)

host: process.env.DB_HOST || 'localhost', // 从环境变量读取配置

port: parseInt(process.env.DB_PORT || '3306'),

username: process.env.DB_USER || 'root',

password: process.env.DB_PASSWORD || 'password',

database: process.env.DB_NAME || 'enterprise_db',

entities: [User], // 注册实体

migrations: ['src/migrations/**/*.ts'], // 迁移文件路径

synchronize: process.env.NODE_ENV === 'development', // 开发环境自动同步表结构(生产禁用!)

logging: true, // 日志输出(开发环境开启,生产可关闭)

});

2.3 仓库(Repository)

定义:实体的“数据访问层”,封装 CRUD 操作,通过getRepository(Entity)获取。

核心 APIfind()/findOne()/save()/remove()/update()等。

示例

// service/user.service.ts

import { AppDataSource } from '../data-source';

import { User } from '../entity/User';

// 获取 User 实体的仓库

const userRepository = AppDataSource.getRepository(User);

// 示例:查询用户

async function getUserById(id: number): Promise<User | null> {

return await userRepository.findOne({ where: { id } });

}

// 示例:创建用户(含密码哈希处理)

async function createUser(userData: Partial<User>): Promise<User> {

const user = userRepository.create(userData); // 实例化实体

// 密码哈希(企业级需使用 bcrypt 等工具)

user.password = await bcrypt.hash(user.password, 10);

return await userRepository.save(user); // 保存到数据库

}

2.4 关系(Relations)

TypeORM 支持四种核心关系类型,通过装饰器定义实体间关联:

关系类型

装饰器

场景

示例

一对一

@OneToOne+@JoinColumn

用户-个人资料(1:1)

@OneToOne(() => Profile)

一对多/多对一

@OneToMany+@ManyToOne

文章-评论(1:N)/ 评论-文章(N:1)

@OneToMany(() => Comment, comment => comment.post)

多对多

@ManyToMany+@JoinTable

学生-课程(N:N)

@ManyToMany(() => Course)

三、企业级最佳实践

3.1 项目结构

推荐按“功能/模块”划分目录,清晰分离实体、仓库、服务、迁移等:

src/

├── entity/ # 实体定义(User.ts, Post.ts...)

├── repository/ # 自定义仓库(扩展基础 Repository)

├── service/ # 业务逻辑层(调用 Repository)

├── migrations/ # 数据库迁移文件

├── data-source.ts # 数据源配置

└── app.ts # 应用入口

3.2 依赖注入集成

Nest.js/Midway 等框架中,通过依赖注入获取仓库,避免硬编码:

// Nest.js 示例(user.service.ts)

import { Injectable } from '@nestjs/common';

import { InjectRepository } from '@nestjs/typeorm';

import { Repository } from 'typeorm';

import { User } from './entity/User';

@Injectable()

export class UserService {

// 注入 User 仓库

constructor(

@InjectRepository(User)

private userRepository: Repository<User>,

) {}

async findAll(): Promise<User[]> {

return this.userRepository.find(); // 直接使用注入的仓库

}

}

3.3 配置管理

核心原则:避免硬编码数据库配置,使用环境变量/配置文件区分环境(开发/测试/生产)。

示例:环境变量 + 配置文件

// config/database.ts

import { DataSourceOptions } from 'typeorm';

import dotenv from 'dotenv';

dotenv.config(); // 加载 .env 文件

export const databaseConfig: DataSourceOptions = {

type: 'postgres',

host: process.env.DB_HOST,

port: parseInt(process.env.DB_PORT || '5432'),

username: process.env.DB_USER,

password: process.env.DB_PASSWORD,

database: process.env.DB_NAME,

entities: ['dist/entity/**/*.js'], // 生产环境使用编译后的 JS 文件

migrations: ['dist/migrations/**/*.js'],

synchronize: false, // 生产环境禁用自动同步!

ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false, // 生产环境启用 SSL

};

3.4 数据库 Schema 管理:迁移(Migration)

核心警告:生产环境禁止使用synchronize: true(可能导致数据丢失),必须通过迁移文件管理表结构变更。

迁移工作流:

1. 创建迁移

npx typeorm-ts-node-commonjs migration:generate src/migrations/CreateUserTable -d src/data-source.ts

(自动根据实体生成迁移文件)

2. 执行迁移

npx typeorm-ts-node-commonjs migration:run -d src/data-source.ts

3. 回滚迁移

npx typeorm-ts-node-commonjs migration:revert -d src/data-source.ts

四、性能与安全优化

4.1 性能优化

1)索引策略

为高频查询字段添加索引,提升查询效率:

import { Entity, Column, Index } from 'typeorm';

@Entity()

export class User {

@Column()

@Index() // 单字段索引

email: string;

@Index(['name', 'email']) // 联合索引(适用于多字段查询)

@Column()

name: string;

}

2)避免 N+1 查询问题

使用relations显式加载关联数据,而非依赖自动加载:

// 优化前(N+1 问题:查询用户后,每条用户记录单独查询关联的 profile)

const users = await userRepository.find();

users.forEach(user => console.log(user.profile)); // 触发 N 次额外查询

// 优化后(一次查询加载所有关联数据)

const users = await userRepository.find({

relations: ['profile'], // 显式加载关联

});

3)QueryBuilder 精确控制 SQL

复杂查询使用QueryBuilder,避免生成冗余 SQL:

// 示例:查询活跃用户并按创建时间排序

const activeUsers = await userRepository

.createQueryBuilder('user')

.where('user.isActive = :isActive', { isActive: true })

.orderBy('user.createTime', 'DESC')

.limit(10)

.getMany();

4.2 安全实践

1)敏感字段处理

密码等敏感字段默认不返回,查询时显式指定字段:

// 实体定义(设置 select: false)

@Column({ select: false })

password: string;

// 查询时如需返回(如登录验证):

const user = await userRepository

.createQueryBuilder('user')

.select(['user.id', 'user.email', 'user.password']) // 显式指定字段

.where('user.email = :email', { email })

.getOne();

2)参数化查询防 SQL 注入

TypeORM 所有查询方法默认支持参数化查询,避免直接拼接 SQL:

// 安全(参数化查询)

const users = await userRepository.find({

where: { email: emailParam }, // 自动参数化

});

// 危险(禁止!)

const users = await userRepository.query(`SELECT * FROM user WHERE email = '${emailParam}'`);

五、进阶技巧

5.1 订阅者(Subscriber)

监听实体生命周期事件(如afterInsert、beforeUpdate),实现审计日志、自动字段更新等:

import { EventSubscriber, EntitySubscriberInterface, InsertEvent } from 'typeorm';

import { User } from './entity/User';

@EventSubscriber()

export class UserSubscriber implements EntitySubscriberInterface<User> {

listenTo() {

return User; // 订阅 User 实体

}

// 插入前自动设置创建人

beforeInsert(event: InsertEvent<User>) {

event.entity.createdBy = 'system'; // 自动填充创建人字段

}

}

5.2 事务处理

使用事务确保多步操作的原子性(全部成功或全部失败):

// 使用 EntityManager 事务

async function transferFunds(fromId: number, toId: number, amount: number) {

return await AppDataSource.transaction(async manager => {

const fromUser = await manager.findOne(User, { where: { id: fromId } });

const toUser = await manager.findOne(User, { where: { id: toId } });

fromUser.balance -= amount;

toUser.balance += amount;

await manager.save(fromUser);

await manager.save(toUser); // 任一保存失败,事务回滚

});

}

5.3 延迟加载(Lazy Relations)

对非高频访问的关联数据使用延迟加载(按需加载):

import { OneToOne } from 'typeorm';

import { Profile } from './Profile';

@Entity()

export class User {

@OneToOne(() => Profile, { lazy: true }) // 延迟加载

profile: Promise<Profile>; // 返回 Promise,需 await 获取

}

// 使用时按需加载

const user = await userRepository.findOne({ where: { id } });

const profile = await user.profile; // 触发关联查询

六、常见挑战与应对

6.1 复杂查询优化

挑战:多表关联、聚合查询性能差。

应对

  • 优先使用QueryBuilder手写优化 SQL;
  • 添加合适的联合索引;
  • 拆分复杂查询为多个简单查询,在应用层聚合结果。

6.2 生产环境配置管理

挑战:多环境(开发/测试/生产)配置切换繁琐。

应对

  • 使用dotenv+config模块管理环境变量;
  • 生产环境配置通过 CI/CD 注入(如 Kubernetes ConfigMap、云服务环境变量)。

6.3 学习曲线陡峭

挑战:关系定义、迁移、QueryBuilder 等概念复杂。

应对

  • 从官方文档的“快速入门”开始,逐步实践实体、仓库、关系基础功能;
  • 复杂场景参考社区示例(如 Nest.js + TypeORM 集成教程)。

七、总结

TypeORM 是企业级 Node.js 应用的强大 ORM 工具,核心价值在于类型安全、丰富的数据库交互能力、框架集成友好。在实际应用中,需重点关注:

  • 生产环境禁用synchronize,使用迁移管理表结构
  • 优化查询性能:索引、显式关联加载、QueryBuilder;
  • 安全实践:敏感字段处理、参数化查询、密码哈希;
  • 工程化配置:环境变量管理、依赖注入集成。

通过本文档的最佳实践,可构建健壮、高效、易维护的企业级数据库交互层。

 

posted on 2025-09-17 09:57  GoGrid  阅读(22)  评论(0)    收藏  举报

导航