TypeORM框架中常用的装饰器
装饰器应用在实体中,而实体是一个转换为数据库表的类
实体装饰器
// 指定实体的名字,也是数据库中表的名字
@Entity("users")
export class User { }
除了给实体设置名字,还能对实体进行配置,让该实体拥有更多得到功能
配置选项:
name
:指定实体对象在数据库中对应的表名。engine
:指定实体对象在数据库中对应的表的存储引擎。database
:指定实体对象在数据库中对应的表所在的数据库名称。schema
:指定实体对象在数据库中对应的表所在的模式名称。synchronize
:指定是否自动同步实体对象和数据库表结构,若为true
则会自动同步,否则需要手动同步(数据库表结构更新中跳过标有false
的实体)orderBy
:指定查询该实体对象时返回结果的排序方式,其属性值为一个对象,包含要排序的属性名和排序方式,如例中的{ name: "ASC", id: "DESC" }
表示按照name
属性升序排列,再按照id
属性降序排列。
@Entity({
name: "users",
engine: "MyISAM",
database: 'example_dev',
schema: 'schema_with_best_tables',
synchronize: false,
orderBy: {
name: "ASC",
id: "DESC"
}
})
export class User { }
代分析码:
- 定义了一个名为
User
的实体对象,该实体对象对应数据库中的users
表,表存储引擎为MyISAM
,所在的数据库名称为example_dev
,所在的模式名称为schema_with_best_tables
,不会自动同步数据库表结构,查询该实体对象时返回结果的排序方式为先按照name
属性升序排列,再按照id
属性降序排列。
列装饰器
-
@Column
- 指定 定义属性对应数据库表中的列
常用
配置选项:
type: ColumnType
表示该属性对应数据库表中的列类型。length: number
表示该属性对应数据库表中的列长度。unique: boolean
true 表示该属性对应数据库表中的列为唯一索引。nullable: boolean
true 表示该属性对应数据库表中的列允许为空。default: boolean
表示该属性对应数据库表中的列,默认值为false
。width: number
表示列类型的显示宽度。update: boolean
表示save
操作是否更新列值。如果为false
,则只能在第一次插入对象时编写该值,默认值为true
。primary: boolean
表示将列标记为主列。 与@PrimaryColumn
使用相同comment: string
表示列的注释。
@Entity("users")
export class User {
@Column({ primary: true })
id: number;
@Column({ type: "varchar", length: 200, unique: true })
firstName: string;
@Column({ nullable: true })
lastName: string;
@Column({ default: false })
isActive: string;
// 简写
@Column('varchar', {
unique: true,
length: 170,
name: 'name',
comment: '业务名称',
})
name: string | null;
}
代码分析:
- 这段代码的作用是定义了一个名为
User
的实体对象,并为该实体对象定义了四个属性,分别是id
、firstName
、lastName
和isActive
,并指定了它们在数据库表中对应的列的一些配置,如数据类型、长度、是否允许为空、默认值等。
-
@PrimaryColumn
- 表示将实体中的属性标记为表主列。
与@column
装饰器相同,但需将其primary
选项设置为 true
@Entity()
export class User {
// id属性列 为主列
@PrimaryColumn()
id: number;
}
-
@PrimaryGeneratedColumn
- 表示将属性标记为值自动生成的主列
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
}
-
@ObjectIdColumn
- 属性标记为 ObjectID
此装饰器仅用于 MongoDB
。 MongoDB 中的每个实体都必须具有 ObjectID 列。
@Entity()
export class User {
@ObjectIdColumn()
id: ObjectID;
}
-
@CreateDateColumn
- 表示该属性对应数据库表中的列为创建时间列
@Entity()
export class User {
@CreateDateColumn()
createdDate: Date;
}
代码分析:
- 这段代码的作用是定义了一个名为
User
的实体对象,并为该实体对象定义了一个名为createdDate
的属性,并指定了它在数据库表中对应的列为创建时间列,即在创建记录时自动将该列的值设置为当前时间。
-
@UpdateDateColumn
- 表示该属性对应数据库表中的列为更新时间列
@Entity()
export class User {
@UpdateDateColumn()
updatedDate: Date;
}
代码分析:
- 这段代码的作用是定义了一个名为
User
的实体对象,并为该实体对象定义了一个名为updatedDate
的属性,并指定了它在数据库表中对应的列为更新时间列,即在更新记录时自动将该列的值设置为当前时间。
关系装饰器
数据库表之间的关系,是操作数据库中极为重要并且复杂的部分,实际应用也是比较多,单独提出来分析,点击下方链接查看(在代码分析中,等待、冷静)
订阅者和监听者装饰器
@AfterLoad
- 表示该方法在实体对象从数据库中加载后被调用
@Entity()
export class Post {
@AfterLoad()
updateCounters() {
if (this.likesCount === undefined) this.likesCount = 0;
}
}
代码分析:
- 这段代码的作用是定义了一个名为
Post
的实体对象,并为该实体对象定义了一个名为updateCounters
的方法,在实体对象从数据库中加载后被调用,用于检查likesCount
属性是否为undefined
,如果是则将其设为0
。
@BeforeInsert
- 表示该方法在实体对象插入到数据库之前被调用
@Entity()
export class Post {
@BeforeInsert()
updateDates() {
this.createdDate = new Date();
}
}
代码分析:
- 这段代码的作用是定义了一个名为
Post
的实体对象,并为该实体对象定义了一个名为updateDates
的方法,该方法在实体对象插入到数据库之前被调用,用于将实体对象的createdDate
属性设置为当前时间。
@AfterInsert
- 表示该方法在实体对象插入到数据库之后被调用。
@Entity()
export class Post {
@AfterInsert()
resetCounters() {
this.counters = 0;
}
}
代码分析:
- 这段代码的作用是定义了一个名为
Post
的实体对象,并为该实体对象定义了一个名为resetCounters
的方法,该方法在实体对象插入到数据库之后被调用,用于将实体对象的counters
属性设置为0
。
@BeforeUpdate
- 表示该方法在实体对象更新到数据库之前被调用
@Entity()
export class Post {
@BeforeUpdate()
updateDates() {
this.updatedDate = new Date();
}
}
代码分析:
- 这段代码的作用是定义了一个名为
Post
的实体对象,并为该实体对象定义了一个名为updateDates
的方法,该方法在实体对象更新到数据库之前被调用,用于将实体对象的updatedDate
属性设置为当前时间。
@AfterUpdate
- 表示该方法在实体对象更新到数据库之后被调用
@Entity()
export class Post {
@AfterUpdate()
updateCounters() {
this.counter = 0;
}
}
代码分析:
- 这段代码的作用是定义了一个名为
Post
的实体对象,并为该实体对象定义了一个名为resetCounters
的方法,该方法在实体对象更新到数据库之后被调用,用于将实体对象的counters
属性设置为0
。
@BeforeRemove
- 表示该方法在实体对象从数据库删除之前被调用
@Entity()
export class Post {
@BeforeRemove()
updateStatus() {
this.status = "removed";
}
}
代码分析:
- 这段代码的作用是定义了一个名为
Post
的实体对象,并为该实体对象定义了一个名为updateDates
的方法,该方法在实体对象从数据库删除之前被调用,用于将实体对象的status
属性设置为removed
。
@AfterRemove
- 表示该方法在实体对象从数据库删除之后被调用
@Entity()
export class Post {
@AfterRemove()
updateStatus() {
this.status = "removed";
}
}
代码分析:
- 这段代码的作用是定义了一个名为
Post
的实体对象,并为该实体对象定义了一个名为resetCounters
的方法,该方法在实体对象从数据库删除之后被调用,用于将实体对象的status
属性设置为removed
。
@BeforeRecover
- 表示该方法在实体对象从软删除状态恢复到普通状态时被调用
在 TypeORM 中,实体对象可以被标记为软删除状态。当实体对象被软删除时,将不会在数据库中被物理删除,而是会被标记为已删除的状态。在后续的查询操作中,软删除的实体对象将不会被查询出来,除非在查询时显式地指定查询已删除的实体对象。当需要恢复已删除的实体对象时,只需要将该实体对象从软删除状态恢复到普通状态即可。
“从软删除状态恢复到普通状态”指的是将一个已经被软删除的实体对象恢复到普通状态,使其可以被查询出来并参与后续的数据库操作
@Entity()
export class Post {
@BeforeRecover()
updateStatus() {
this.status = "recovered";
}
}
代码分析:
- 这段代码的作用是定义了一个名为
Post
的实体对象,并为该实体对象定义了一个名为updateStatus
的方法,该方法在实体对象从软删除状态恢复到普通状态时被调用,用于将实体对象的status
属性设置为"recovered"
。
@AfterRecover
- 表示该方法在实体对象从软删除状态恢复到普通状态时被调用
@Entity()
export class Post {
@AfterRecover()
updateStatus() {
this.status = "recovered";
}
}
代码分析:
- 这段代码的作用是定义了一个名为
Post
的实体对象,并为该实体对象定义了一个名为updateStatus
的方法,该方法在实体对象从软删除状态恢复到普通状态时被调用,用于将实体对象的status
属性设置为"recovered"
。
@EventSubscriber
- 表示定义一个实体订阅者用
@EventSubscriber()
export class PostSubscriber implements EntitySubscriberInterface<Post> {
/**
* 表示此订阅者仅侦听Post事件。
*/
listenTo() {
// 只有返回的事件,才是监听的实体
return Post;
}
/**
* 在POST INSERTED之前调用。
*/
beforeInsert(event: InsertEvent<Post>) {
console.log(`BEFORE POST INSERTED: `, event.entity);
}
}
代码分析:
- 这段代码的作用是定义了一个名为
PostSubscriber
的实体订阅者,用于处理Post
实体对象在数据库中的各种事件,其中通过listenTo
方法指定该订阅者只关注Post
实体对象的事件,在beforeInsert
方法中处理Post
实体对象在插入数据库之前的事件,输出日志信息和要插入的实体对象。
特殊装饰器(高级装饰器)
@Index
- 为实体对象中的多个属性添加联合索引
联合索引是指在数据库中为多个列添加的一个索引,用于提高查询效率和优化数据库的性能
@Entity()
// 表示在数据库中为 `firstName` 和 `lastName` 这两列添加一个联合索引
@Index(["firstName", "lastName"])
@Index(["lastName", "middleName"])
// 表示在数据库中为 `firstName`、`lastName` 和 `middleName` 这三列添加一个联合唯一索引
@Index(["firstName", "lastName", "middleName"], { unique: true })
export class User {
@Column()
firstName: string;
@Column()
lastName: string;
@Column()
middleName: string;
}
代码分析:
- 这段代码的作用是定义了一个名为
User
的实体对象,并为该实体对象定义了三个索引,分别为firstName
和lastName
的联合索引,lastName
和middleName
的联合索引,以及firstName
、lastName
和middleName
的联合唯一索引,用于提高查询效率和保证数据的唯一性。
@Unique
- 为实体对象中的某个属性或多个属性组合起来添加唯一约束
唯一约束是指在数据库中为某个列或多个列添加一个限制条件,用于保证该列或这些列组合起来的唯一性,避免重复数据的出现
@Entity()
@Unique(["firstName"])
// 表示在数据库中为 `lastName` 和 `middleName` 这两列组合起来增加一个唯一约束,用于保证这两列组合起来的唯一性
@Unique(["lastName", "middleName"])
// 表示在数据库中为 `firstName`、`lastName` 和 `middleName` 这三列组合起来增加一个名为 `UQ_NAMES` 的唯一约束,用于保证这三列组合起来的唯一性
@Unique("UQ_NAMES", ["firstName", "lastName", "middleName"])
export class User {
@Column()
firstName: string;
@Column()
lastName: string;
@Column()
middleName: string;
}
事务装饰器
@Transaction
,@TransactionManager
和@TransactionRepository
使用 @Transaction()
装饰器时,将要执行的方法标记为事务方法,所以也被称为事务装饰器
。
@Transaction()
save(@TransactionManager() manager: EntityManager, user: User) {
return manager.save(user);
}
上方的方法是一个实体对象的保存方法,使用
@Transaction()
装饰器将该方法标记为一个事务方法。该方法有两个参数,第一个参数是一个EntityManager
对象,用于处理数据库事务,第二个参数是一个User
对象,表示要保存的实体对象。在方法体中,通过manager.save(user)
方法将实体对象保存到数据库中,并返回保存后的实体对象。
@Transaction()
save(user: User, @TransactionRepository(User) userRepository: Repository<User>) {
return userRepository.save(user);
}
上方的方法也是一个实体对象的保存方法,同样使用
@Transaction()
装饰器将该方法标记为一个事务方法。该方法有两个参数,第一个参数是一个User
对象,表示要保存的实体对象,第二个参数是一个Repository<User>
对象,用于操作数据库中的User
表。在方法体中,通过userRepository.save(user)
方法将实体对象保存到数据库中,并返回保存后的实体对象。
@Transaction()
save(@QueryParam("name") name: string, @TransactionRepository() userRepository: UserRepository) {
return userRepository.findByName(name);
}
上方的方法是一个查询方法,同样使用
@Transaction()
装饰器将该方法标记为一个事务方法。该方法有两个参数,第一个参数是一个string
类型的name
,表示要查询的实体对象的名称,第二个参数是一个UserRepository
对象,用于操作数据库中的User
表。在方法体中,通过userRepository.findByName(name)
方法查询数据库中符合条件的实体对象,并返回查询结果。
自定义存储库装饰器
@EntityRepository
- 用于自定义实体的存储库。
使用getCustomRepository()
方法获取任何自定义创建的存储库。
创建自定义存储库
方法一:继承 Repository
类实现创建自定义存储库
import { EntityRepository, Repository } from "typeorm";
import { User } from "../entity/User";
@EntityRepository(User)
export class UserRepository extends Repository<User> {
findByName(firstName: string, lastName: string) {
return this.findOne({ firstName, lastName });
}
}
使用:
import { getCustomRepository } from "typeorm";
import { UserRepository } from "./repository/UserRepository";
const userRepository = getCustomRepository(UserRepository); // 或connection.getCustomRepository或manager.getCustomRepository()
const user = userRepository.create(); // 和 const user = new User();一样
user.firstName = "Timber";
user.lastName = "Saw";
await userRepository.save(user);
const timber = await userRepository.findByName("Timber", "Saw");
方法二:继承 AbstractRepository
类实现创建自定义存储库
import { EntityRepository, AbstractRepository } from "typeorm";
import { User } from "../entity/User";
@EntityRepository(User)
export class UserRepository extends AbstractRepository<User> {
createAndSave(firstName: string, lastName: string) {
const user = new User();
user.firstName = firstName;
user.lastName = lastName;
return this.manager.save(user);
}
findByName(firstName: string, lastName: string) {
return this.repository.findOne({ firstName, lastName });
}
}
使用:
import { getCustomRepository } from "typeorm";
import { UserRepository } from "./repository/UserRepository";
const userRepository = getCustomRepository(UserRepository); // or connection.getCustomRepository or manager.getCustomRepository()
await userRepository.createAndSave("Timber", "Saw");
const timber = await userRepository.findByName("Timber", "Saw");
这两种方式在实现自定义存储库时有以下区别:
Repository
类是 TypeORM 提供的默认存储库类,它包含了一些常用的实体操作方法,例如save()
、delete()
、findOne()
等。如果选择继承Repository
类,则可以直接使用这些内置方法进行数据库操作,无需重复编写这些方法。此外,还可以在自定义存储库中添加其他自定义方法来扩展功能。
AbstractRepository
类是一个抽象类,它没有内置的实体操作方法,需要手动实现所有数据库操作方法。如果选择继承AbstractRepository
类,则需要自己实现所有数据库操作方法,包括save()
、delete()
、findOne()
等。同时,也可以在自定义存储库中添加其他自定义方法来扩展功能。 因此,选择继承Repository
类可以更快速地实现自定义存储库,但是可能存在一些性能上的损失,因为该类中包含了很多通用的代码;而选择继承AbstractRepository
类则需要手动实现所有数据库操作方法,但是可以更加灵活地控制存储库的行为和性能。
获取并操作数据库数据
转载自:https://juejin.cn/post/7238254724863524922