TypeOrm总结笔记

一个TypeOrm实体例子

import { AfterLoad, BeforeInsert, BeforeUpdate } from 'typeorm';
import moment from 'moment';
import { getCurrentUser } from './current-user';
import { Column, PrimaryGeneratedColumn, VersionColumn } from './column-decorator';

export abstract class BaseEntity {
    @PrimaryGeneratedColumn({ name: "id", type: "int" })
    public id?: number;

    @Column('varchar', { nullable: true, name: 'createdUser', length: 255 })
    public createdUser?: string;

    @Column({ type: 'timestamp', name: 'createdDate', nullable: true })
    public createdDate?: Date;

    @Column('varchar', { nullable: true, name: 'modifiedUser', length: 255 })
    public modifiedUser?: string;

    @Column({ type: 'timestamp', name: 'modifiedDate', nullable: true })
    public modifiedDate?: Date;

    @Column('bit', { name: 'isDeleted', default: false })
    public isDeleted?: boolean;

    @VersionColumn({ name: 'versionNumber', nullable: true })
    public versionNumber?: number;

    @BeforeInsert()
    public beforeInsertListener() {
        const currentUser = getCurrentUser();
        this.createdDate = moment()
            .utc()
            .toDate();
        this.createdUser = currentUser && currentUser.userName;
    }

    @BeforeUpdate()
    public beforeUpdateListener() {
        const currentUser = getCurrentUser();
        this.modifiedDate = moment()
            .utc()
            .toDate();
        this.modifiedUser = currentUser && currentUser.userName;
    }
    @AfterLoad()
    public afterloader() {
        // const t = this;
    }
}

这段代码定义了一个抽象的 BaseEntity 类,结合了 TypeORM 中常见的实体管理功能。它涉及多个 TypeORM 的知识点,包括装饰器、生命周期钩子、列定义、版本控制等内容。

1. 实体类

  • TypeORM 的实体类用于表示数据库表。
  • 每个实体类对应数据库中的一张表,类的属性对应表中的列。
  • 通过在类中使用装饰器,定义字段属性及其数据库约束。

示例

export abstract class BaseEntity {
    // 定义了一个抽象的实体类,不能直接实例化,只能被其他实体类继承
}

2. 列装饰器

2.1 @PrimaryGeneratedColumn
  • 用于定义主键列,主键是自动生成的。
  • 属性:
    • name: 列名。
    • type: 数据类型。
  • 主键通常用于标识每行记录的唯一性。

代码

@PrimaryGeneratedColumn({ name: "id", type: "int" })
public id?: number;//这是实体类中属性的名字,用于在代码中操作这个字段。
//在数据库操作中,TypeORM 会将这个属性名映射到 id所指定的数据库字段名。
2.2 @Column
  • 定义普通列,可以指定数据类型和其他约束。
  • 属性:
    • type: 列的数据类型。
    • nullable: 是否允许为 null
    • name: 列名。
    • length: 字符串列的长度。
    • default: 默认值。

代码

'varchar'type: 'varchar' 是完全等价的,只是写法不同
@Column('varchar', { nullable: true, name: 'createdUser', length: 255 })
public createdUser?: string;
2.3 @VersionColumn
  • 定义一个用于控制版本的列,通常用于并发更新时的乐观锁。
  • 每次更新记录时,版本号自动递增。

代码

@VersionColumn({ name: 'versionNumber', nullable: true })
public versionNumber?: number;

3. 生命周期钩子

  • TypeORM 提供的装饰器,用于在实体的生命周期中执行逻辑。
  • 这些钩子是在特定操作之前或之后调用的。
3.1 @BeforeInsert
  • 在执行 INSERT 操作之前触发。
  • 常用于设置默认值、验证数据或执行其他逻辑。

代码

@BeforeInsert()
public beforeInsertListener() {
    const currentUser = getCurrentUser();
    this.createdDate = moment().utc().toDate(); // 设置创建时间
    this.createdUser = currentUser && currentUser.userName; // 设置创建用户
}
3.2 @BeforeUpdate
  • 在执行 UPDATE 操作之前触发。
  • 常用于更新修改时间或记录修改用户。

代码

@BeforeUpdate()
public beforeUpdateListener() {
    const currentUser = getCurrentUser();
    this.modifiedDate = moment().utc().toDate(); // 设置修改时间
    this.modifiedUser = currentUser && currentUser.userName; // 设置修改用户
}
3.3 @AfterLoad
  • 在从数据库加载实体后触发。
  • 常用于处理从数据库读取后的逻辑。

代码

@AfterLoad()
public afterloader() {
    // 从数据库加载实体后执行
}

4. 抽象类

  • 抽象类不能直接实例化,只能被其他实体类继承。
  • 常用于定义通用属性和逻辑(如审计字段、版本控制等)。

代码

export abstract class BaseEntity { ... }

5. 工具函数

  • moment.utc().toDate():使用 Moment.js 获取当前 UTC 时间。
  • getCurrentUser():从上下文中获取当前用户,常用于审计功能。

代码

const currentUser = getCurrentUser();
this.createdDate = moment().utc().toDate();

6. 全部钩子函数总结

钩子函数触发时机常见用途
@BeforeInsert插入之前设置默认值、数据校验
@AfterInsert插入之后日志记录、通知触发
@BeforeUpdate更新之前更新时间戳、校验数据
@AfterUpdate更新之后日志记录、触发事件
@BeforeRemove删除之前校验权限、检查数据完整性
@AfterRemove删除之后日志记录、清理缓存
@AfterLoad实体加载后数据解密、格式化

注意事项

  1. 钩子触发条件

    • 钩子函数只在使用 TypeORM 方法(如 saveremove)时触发。
    • 如果直接执行原生 SQL 查询,钩子不会触发。
  2. 性能

    • 如果实体类中包含复杂逻辑或处理大量数据,使用钩子可能会影响性能。
  3. 事务支持

    • 钩子函数运行在事务中,钩子中发生错误会回滚整个事务。


TypeORM 提供了丰富的 API,用于操作数据库中的实体和执行复杂的查询。以下是常用的 API 按功能分类的总结:


1. 基础查询

1.1 findOne
  • 作用:根据条件查询单个实体。
  • 签名
    findOne(options: FindOneOptions): Promise<Entity | null>;
    
  • 示例
    const user = await userRepository.findOne({ where: { id: 1 } });
    
1.2 find
  • 作用:根据条件查询多个实体。
  • 签名
    find(options?: FindManyOptions): Promise<Entity[]>;
    
  • 示例
    const users = await userRepository.find({ where: { isActive: true } });
    
1.3 findBy
  • 作用:简化版 find,直接传入条件对象。
  • 示例
    const users = await userRepository.findBy({ role: 'admin' });
    
1.4 findOneBy
  • 作用:简化版 findOne,直接传入条件对象。
  • 示例
    const user = await userRepository.findOneBy({ email: 'test@example.com' });
    

2. 插入和更新

2.1 save
  • 作用:保存或更新实体。
  • 签名
    save(entity: Entity | Entity[]): Promise<Entity[]>;
    
  • 示例
    const newUser = await userRepository.save({ name: 'John', age: 25 });
    
2.2 insert
  • 作用:仅插入新记录,不更新已有记录。
  • 签名
    insert(entity: QueryDeepPartialEntity<Entity>): Promise<InsertResult>;
    
  • 示例
    await userRepository.insert({ name: 'John', age: 25 });
    
2.3 update
  • 作用:根据条件更新记录。
  • 签名
    update(criteria: FindOptionsWhere<Entity>, partialEntity: QueryDeepPartialEntity<Entity>): Promise<UpdateResult>;
    
  • 示例
    await userRepository.update({ id: 1 }, { age: 26 });
    

3. 删除

3.1 delete
  • 作用:根据条件删除记录。
  • 签名
    delete(criteria: FindOptionsWhere<Entity>): Promise<DeleteResult>;
    
  • 示例
    await userRepository.delete({ id: 1 });
    
3.2 softRemove
  • 作用:执行软删除(将实体标记为已删除,而不真正从数据库中移除)。
  • 示例
    await userRepository.softRemove(entity);
    
3.3 restore
  • 作用:恢复软删除的实体。
  • 示例
    await userRepository.restore(1);
    

4. 高级查询

4.1 count
  • 作用:根据条件统计记录数。
  • 示例
    const totalUsers = await userRepository.count({ where: { isActive: true } });
    
4.2 query
  • 作用:执行原生 SQL 查询。
  • 示例
    const rawResult = await dataSource.query('SELECT * FROM users WHERE id = $1', [1]);
    
4.3 createQueryBuilder
  • 作用:构建复杂查询。
  • 示例
    const users = await userRepository
      .createQueryBuilder('user')
      .where('user.isActive = :isActive', { isActive: true })
      .getMany();
    

5. 事务

5.1 transaction
  • 作用:显式管理事务。
  • 示例
    await dataSource.transaction(async (manager) => {
      await manager.save(User, { name: 'Jane' });
      await manager.update(Profile, { userId: 1 }, { bio: 'Hello!' });
    });
    
5.2 QueryRunner
  • 作用:手动管理事务。
  • 示例
    const queryRunner = dataSource.createQueryRunner();
    await queryRunner.startTransaction();
    try {
      await queryRunner.manager.save(User, { name: 'Jane' });
      await queryRunner.commitTransaction();
    } catch (err) {
      await queryRunner.rollbackTransaction();
    } finally {
      await queryRunner.release();
    }
    

6. 工具方法

6.1 create
  • 作用:创建一个实体实例,但不保存到数据库。
  • 示例
    const user = userRepository.create({ name: 'John' });
    
6.2 preload
  • 作用:加载现有实体并合并新数据,如果不存在则返回 undefined
  • 示例
    const user = await userRepository.preload({ id: 1, name: 'Updated Name' });
    
6.3 merge
  • 作用:合并多个对象到一个实体。
  • 示例
    const user = userRepository.merge(existingUser, { name: 'New Name' });
    

常见组合场景

  1. 创建或更新用户

    const user = await userRepository.save({ id: 1, name: 'John' });
    
  2. 查询用户和统计数量

    const users = await userRepository.find({ where: { isActive: true } });
    const count = await userRepository.count({ where: { isActive: true } });
    
  3. 软删除用户

    await userRepository.softRemove(user);
    

在 TypeORM 中,排序和分组功能通过查询条件或者 QueryBuilder 实现。以下是具体的使用方式:


1. 排序

使用 findfindOne
  • 通过 order 属性进行排序
  • 示例
    const users = await userRepository.find({
      order: {
        age: 'ASC', // 按年龄升序
        name: 'DESC', // 按名称降序
      },
    });
    
使用 QueryBuilder
  • 可以指定排序字段和顺序
  • 示例
    const users = await userRepository
      .createQueryBuilder('user')
      .orderBy('user.age', 'ASC') // 按年龄升序
      .addOrderBy('user.name', 'DESC') // 再按名称降序
      .getMany();
    

2. 分组

使用 QueryBuildergroupBy
  • 分组查询可以结合聚合函数(如 COUNTSUM 等)使用

  • 示例

    const result = await userRepository
      .createQueryBuilder('user')
      .select('user.role') // 分组字段
      .addSelect('COUNT(user.id)', 'count') // 聚合结果
      .groupBy('user.role') // 按角色分组
      .getRawMany(); // 返回原生查询结果
    
  • 结果示例

    [
      { role: 'admin', count: 10 },
      { role: 'user', count: 50 }
    ]
    
分组后结合排序
  • 在分组基础上可以使用 orderBy 排序分组结果
  • 示例
    const result = await userRepository
      .createQueryBuilder('user')
      .select('user.role')
      .addSelect('COUNT(user.id)', 'count')
      .groupBy('user.role')
      .orderBy('count', 'DESC') // 按数量降序
      .getRawMany();
    

3. 聚合函数

  • TypeORM 支持多种聚合函数,常结合 QueryBuilder 使用

    • COUNT:统计数量
    • SUM:求和
    • AVG:求平均值
    • MIN:最小值
    • MAX:最大值
  • 示例:按年龄分组计算平均工资

    const result = await userRepository
      .createQueryBuilder('user')
      .select('user.age')
      .addSelect('AVG(user.salary)', 'avgSalary')
      .groupBy('user.age')
      .orderBy('user.age', 'ASC')
      .getRawMany();
    
  • 结果示例

    [
      { age: 25, avgSalary: 3000 },
      { age: 30, avgSalary: 5000 }
    ]
    

4. 多条件复杂查询

查询示例
  • 查询所有角色为 admin 且按创建日期倒序排列的用户。
    const users = await userRepository.find({
      where: { role: 'admin' },
      order: {
        createdDate: 'DESC',
      },
    });
    
复杂分组和筛选
  • 查询每个角色的用户数量,但只显示用户数大于 5 的角色。
    const result = await userRepository
      .createQueryBuilder('user')
      .select('user.role')
      .addSelect('COUNT(user.id)', 'count')
      .groupBy('user.role')
      .having('COUNT(user.id) > :minCount', { minCount: 5 }) // 筛选条件
      .orderBy('count', 'DESC')
      .getRawMany();
    

总结

  • 排序order 用于基础查询,QueryBuilderorderBy 用于复杂查询。
  • 分组QueryBuildergroupByhaving 支持灵活分组和筛选。
  • 聚合:支持 COUNTSUMAVG 等常见函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值