一个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 | 实体加载后 | 数据解密、格式化 |
注意事项
-
钩子触发条件:
- 钩子函数只在使用
TypeORM
方法(如save
、remove
)时触发。 - 如果直接执行原生 SQL 查询,钩子不会触发。
- 钩子函数只在使用
-
性能:
- 如果实体类中包含复杂逻辑或处理大量数据,使用钩子可能会影响性能。
-
事务支持:
- 钩子函数运行在事务中,钩子中发生错误会回滚整个事务。
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' });
常见组合场景
-
创建或更新用户:
const user = await userRepository.save({ id: 1, name: 'John' });
-
查询用户和统计数量:
const users = await userRepository.find({ where: { isActive: true } }); const count = await userRepository.count({ where: { isActive: true } });
-
软删除用户:
await userRepository.softRemove(user);
在 TypeORM 中,排序和分组功能通过查询条件或者 QueryBuilder
实现。以下是具体的使用方式:
1. 排序
使用 find
或 findOne
- 通过
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. 分组
使用 QueryBuilder
的 groupBy
-
分组查询可以结合聚合函数(如
COUNT
、SUM
等)使用。 -
示例:
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
用于基础查询,QueryBuilder
的orderBy
用于复杂查询。 - 分组:
QueryBuilder
的groupBy
和having
支持灵活分组和筛选。 - 聚合:支持
COUNT
、SUM
、AVG
等常见函数。