ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

TypeORM核心方法全解析:从基础操作到高级查询

TypeORM核心方法全解析:从基础操作到高级查询 1. TypeORM 方法全景解析TypeORM 作为 Node.js 生态中最流行的 ORM 框架之一提供了丰富的 API 方法来操作数据库。对于初学者来说面对众多的方法往往会感到困惑。本文将系统梳理 TypeORM 的核心方法帮助开发者快速掌握其使用精髓。1.1 实体操作方法实体Entity是 TypeORM 的核心概念对应数据库中的表。以下是实体操作的关键方法// 创建实体实例 const user new User() user.name 张三 user.age 25 // 保存实体 await connection.manager.save(user) // 或使用 Repository await userRepository.save(user) // 查询单个实体 const firstUser await userRepository.findOne(1) // 按ID查询 const specificUser await userRepository.findOne({ name: 张三 }) // 按条件查询 // 查询多个实体 const allUsers await userRepository.find() const activeUsers await userRepository.find({ isActive: true }) // 删除实体 await userRepository.remove(user) // 或按条件删除 await userRepository.delete({ isActive: false })提示save() 方法既可以用于新增也可以用于更新它会自动检测实体是否已存在。1.2 查询构建器QueryBuilderQueryBuilder 是 TypeORM 最强大的功能之一允许构建复杂查询const users await connection .getRepository(User) .createQueryBuilder(user) .where(user.age :age, { age: 18 }) .andWhere(user.isActive :isActive, { isActive: true }) .orderBy(user.name, ASC) .skip(10) .take(5) .getMany()QueryBuilder 支持的方法包括select() - 指定查询字段where()/andWhere()/orWhere() - 条件查询orderBy() - 排序groupBy()/having() - 分组leftJoinAndSelect()/innerJoinAndSelect() - 关联查询skip()/take() - 分页1.3 关联关系操作TypeORM 支持多种关联关系每种关系都有对应的操作方法1.3.1 一对一关系// 定义关系 Entity() export class Profile { OneToOne(() User, user user.profile) user: User } // 查询时加载关联 const userWithProfile await userRepository.findOne(1, { relations: [profile] }) // 使用QueryBuilder加载 const users await userRepository .createQueryBuilder(user) .leftJoinAndSelect(user.profile, profile) .getMany()1.3.2 一对多/多对一关系// 用户有多篇文章 Entity() export class User { OneToMany(() Article, article article.author) articles: Article[] } // 查询用户及其文章 const userWithArticles await userRepository.findOne(1, { relations: [articles] }) // 添加关联文章 const article new Article() article.title TypeORM指南 user.articles [article] await userRepository.save(user)1.3.3 多对多关系// 用户和角色多对多 Entity() export class User { ManyToMany(() Role) JoinTable() roles: Role[] } // 添加角色关联 const role new Role() role.name 管理员 user.roles [role] await userRepository.save(user) // 查询带角色的用户 const users await userRepository.find({ relations: [roles] })2. 事务与批量操作2.1 事务处理TypeORM 提供了多种事务管理方式// 方式1使用事务装饰器 Transaction() async updateUser(TransactionManager() manager: EntityManager, user: User) { await manager.save(user) await manager.save(Log, { action: update user }) } // 方式2显式使用事务 await connection.transaction(async manager { await manager.save(user1) await manager.save(user2) }) // 方式3使用QueryRunner const queryRunner connection.createQueryRunner() await queryRunner.startTransaction() try { await queryRunner.manager.save(user1) await queryRunner.manager.save(user2) await queryRunner.commitTransaction() } catch (err) { await queryRunner.rollbackTransaction() } finally { await queryRunner.release() }2.2 批量操作对于大量数据操作TypeORM 提供了高效的方法// 批量插入 await connection .createQueryBuilder() .insert() .into(User) .values([ { name: 张三, age: 25 }, { name: 李四, age: 30 } ]) .execute() // 批量更新 await connection .createQueryBuilder() .update(User) .set({ isActive: true }) .where(age :age, { age: 18 }) .execute() // 批量删除 await connection .createQueryBuilder() .delete() .from(User) .where(isActive :isActive, { isActive: false }) .execute()3. 高级查询技巧3.1 子查询const users await userRepository .createQueryBuilder(user) .where(qb { const subQuery qb .subQuery() .select(article.userId) .from(Article, article) .where(article.likes 100) .getQuery() return user.id IN subQuery }) .getMany()3.2 原生SQL查询// 使用QueryBuilder执行原生SQL const rawData await connection .createQueryBuilder() .select(*) .from(User, user) .where(user.age :age, { age: 18 }) .getRawMany() // 直接执行SQL const users await connection.query( SELECT * FROM users WHERE age $1, [18] )3.3 缓存查询const users await userRepository.find({ where: { isActive: true }, cache: true // 启用缓存 }) // 带ID的缓存 const user await userRepository.findOne(1, { cache: { id: user_1, milliseconds: 30000 } // 30秒缓存 })4. 数据迁移与同步4.1 数据库同步// 自动同步实体到数据库 await connection.synchronize() // 同步选项 await connection.synchronize({ dropSchema: true, // 删除现有表结构 migrationsRun: true // 运行迁移 })注意生产环境慎用 synchronize()建议使用迁移4.2 数据迁移// 创建迁移 typeorm migration:create -n CreateUserTable // 运行迁移 typeorm migration:run // 回滚迁移 typeorm migration:revert // 生成迁移(根据实体变更自动生成) typeorm migration:generate -n SchemaChanges5. 性能优化技巧5.1 延迟关系加载Entity() export class User { ManyToMany(() Role, { lazy: true }) JoinTable() roles: PromiseRole[] } // 使用时 const user await userRepository.findOne(1) const roles await user.roles // 实际加载关系5.2 查询部分字段// 只选择需要的字段 const users await userRepository.find({ select: [id, name], where: { isActive: true } }) // 使用QueryBuilder const users await userRepository .createQueryBuilder(user) .select([user.id, user.name]) .getMany()5.3 批量插入优化// 使用事务批量插入 await connection.transaction(async manager { const batchSize 1000 for (let i 0; i users.length; i batchSize) { const batch users.slice(i, i batchSize) await manager.save(batch) } })6. 常见问题与解决方案6.1 N1 查询问题// 错误方式 - 会导致N1查询 const users await userRepository.find() for (const user of users) { const articles await user.articles // 每次循环都查询 } // 正确方式1 - 预加载关系 const users await userRepository.find({ relations: [articles] }) // 正确方式2 - 使用QueryBuilder const users await userRepository .createQueryBuilder(user) .leftJoinAndSelect(user.articles, articles) .getMany()6.2 循环引用问题// 使用RelationId避免循环引用 Entity() export class User { OneToMany(() Article, article article.author) articles: Article[] RelationId((user: User) user.articles) articleIds: number[] // 只存储ID而不加载整个关系 }6.3 大表分页优化// 使用游标分页代替offset分页 const lastId 100 // 上一页最后一条记录的ID const users await userRepository .createQueryBuilder(user) .where(user.id :lastId, { lastId }) .orderBy(user.id, ASC) .take(10) .getMany()7. 实用工具方法7.1 监听实体事件EventSubscriber() export class UserSubscriber implements EntitySubscriberInterfaceUser { listenTo() { return User } afterLoad(user: User) { console.log(用户 ${user.id} 已加载) } beforeInsert(user: User) { user.createdAt new Date() } }7.2 自定义存储库EntityRepository(User) export class UserRepository extends RepositoryUser { findActiveUsers(): PromiseUser[] { return this.find({ where: { isActive: true } }) } } // 使用自定义存储库 const userRepository connection.getCustomRepository(UserRepository) const activeUsers await userRepository.findActiveUsers()7.3 数据验证Entity() export class User { Column() Length(2, 50) // 用户名长度2-50 name: string Column() IsEmail() // 必须是邮箱格式 email: string Column() Min(18) // 最小年龄18 age: number } // 验证实体 const errors await validate(user) if (errors.length 0) { // 处理验证错误 }8. 最佳实践总结存储库模式尽量使用 Repository 模式而非直接使用 EntityManager提高代码可维护性事务边界将业务操作封装在事务中确保数据一致性查询优化避免 SELECT *合理使用 JOIN对大表使用游标分页环境区分开发环境可使用 synchronize生产环境必须使用迁移性能监控// 启用查询日志 new DataSource({ logging: [query, error], logger: advanced-console })类型安全充分利用 TypeScript 类型检查定义实体和关系的准确类型测试策略使用内存数据库进行单元测试测试包含事务回滚场景通过系统掌握这些方法开发者可以充分发挥 TypeORM 的强大功能构建高效可靠的数据库应用。记住方法虽多但核心思想是一致的以面向对象的方式操作数据库让数据访问层更加直观和易于维护。
返回列表