环境信息:Node.js 20.x、TypeORM 0.3.x、MySQL 8.x、@nestjs/typeorm 10.x
一、对于ORM的理解#
面向对象编程和关系型数据库,都是目前最流行的技术,但是它们的模型是不一样的。
面向对象编程把所有实体看成对象(object),关系型数据库则是采用实体之间的关系(relation)连接数据。很早就有人提出,关系也可以用对象表达,这样的话,就能使用面向对象编程,来操作关系型数据库。

简单说,ORM 就是通过实例对象的语法,完成关系型数据库的操作的技术,是”对象-关系映射”(Object/Relational Mapping) 的缩写。
ORM 把数据库映射成对象
- 数据库的表(table) —> 类(class)
- 记录(record,行数据)—> 对象(object)
- 字段(field)—> 对象的属性(attribute)
举例来说,下面是一行 SQL 语句。
SELECT id, first_name, last_name, phone, birth_date, sexFROM personsWHERE id = 10如果程序直接操作SQL,大致语法如下:
res = db.execSql(sql)name = res[0]['FIRST_NAME']如果改成 ORM 的写法大致如下:
p = Person.get(10)name = p.first_name一比较就可以发现,ORM 使用对象,封装了数据库操作,因此可以不碰 SQL 语言。开发者只使用面向对象编程,与数据对象直接交互,不用关心底层数据库。
ORM 有下面这些优点:
- 数据模型都在一个地方定义,更容易更新和维护,也利于重用代码。
- ORM 有现成的工具,很多功能都可以自动完成,比如数据消毒、预处理、事务等等。
- 它迫使你使用 MVC 架构,ORM 就是天然的 Model,最终使代码更清晰。
- 基于 ORM 的业务代码比较简单,代码量少,语义性好,容易理解。
- 你不必编写性能不佳的 SQL。
但是,ORM 也有很突出的缺点。
- ORM 库不是轻量级工具,需要花精力学习和设置。
- 对于复杂的查询,ORM 要么是无法表达,要么是性能不如原生的 SQL。
- ORM 抽象掉了数据库层,开发者无法了解底层的数据库操作,也无法定制一些特殊的 SQL。
二、TypeORM#
TypeORM(中文文档)是一个ORM,可以在 NodeJS、浏览器、Cordova、PhoneGap、Ionic、React Native、NativeScript、Expo 和 Electron 平台上运行,并且可以与 TypeScript 和 JavaScript(ES5、ES6、ES7、ES8)一起使用。
TypeORM 受到其他 ORM 的很大影响,例如 Hibernate、Doctrine 和 Entity Framework。
直接开始#
直接运行命令创建TypeORM项目
npx typeorm init --name typeorm-test --database mysql2没有安装TypeORM包的话,使用npx命令时,系统会自动检测是否已经安装这个包。
Terminal window npm http fetch GET 200 https://registry.npmjs.org/npm 729ms (cache updated)npm http fetch GET 200 https://registry.npmjs.org/typeorm 1538ms (cache miss)Need to install the following packages:typeorm@0.3.20Ok to proceed? (y) y选择“y”后,脚本将自动安装相关的依赖包
不过项目并不会自动帮你安装mysql的依赖包,所以项目创建好之后,还需在项目中安装mysql2的相关依赖
npm i mysql2 --save
创建模型及实体#
要处理数据库,首先当然要创建表,在TypeORM中,我们通过定义实体来告诉他如何创建一个数据表,比如,我们要创建一个用户表,就需要定义用户的实体模型
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
@Entity()export class User { @PrimaryGeneratedColumn() id: number
@Column({ length: 100 }) name: string
@Column({ length: 100 }) nickname: string
@Column() age: number
@Column({ length: 11 }) phone: string
@Column('text') desc: string
@Column('double', { default: 0, }) other: number}我们仅仅定义一个User模型并不够,还需要使用装饰器@Entity将其定义为一个实体
用于装饰列的装饰器包括 @Column、@PrimaryGeneratedColumn和@PrimaryColumn。@PrimaryColumn用于定义普通主键列,而@PrimaryGeneratedColumn用于定义自增主键列。
string类型默认映射为数据库中的varchar(255),number类型默认映射为int整数类型。如果我们希望某个字段是文本(Text)类型或者(Double)类型,可以通过@Column装饰器指定
连接数据库#
为了和数据库进行交互,我们需要设置一个数据源(DataSource)。数据源中包含了与数据库连接相关的配置。通过这些配置,我们可以建立与数据库的初始连接或者连接池。
import 'reflect-metadata'import { DataSource } from 'typeorm'import { User } from './entity/User'
export const AppDataSource = new DataSource({ type: 'mysql', host: 'localhost', port: 3306, username: 'root', password: '123456', database: 'typeorm_test', synchronize: true, logging: true, entities: [User], migrations: [], subscribers: [],})- type :表示数据库的类型,TypeORM 不只支持 MySQL 还支持 postgres、oracle、sqllite 等数据库。
- database:表示你需要连接的数据库名,mysql 是可以有多个
database(schema)的。**注意:**数据库并不能帮你创建,需要自己先创建数据库 - host、port :表示数据库服务器的主机和端口号
- user、password:表示登录数据库的用户名和密码
- synchronize :表示根据同步建表,简单来说就是当 database 里没有和 Entity 对应的表的时候,会自动生成建表 sql 语句并执行
- logging:启动日志记录,会打印生成的 sql 语句
- entities :指定有哪些和数据库的表对应的 Entity,也可以通过路径的方式指定,也可以用通配符,比如:
entities: ['./**/entity/*.ts']
还有一些其他的配置:
- migrations :修改表结构之类的 sql
- subscribers:Entity 生命周期的订阅者,比如 insert、update、remove 前后,可以加入一些逻辑
- poolSize:指定数据库连接池中连接的最大数量
- connectorPackage:指定驱动包
创建DataSource实例之后,需要调用该实例的initialize方法来创建数据库连接。
使用实体管理器操作数据库#
一旦数据库连接成功,我们就可以使用实体管理器(manager)来执行数据库操作,在src/index.ts文件下,稍作修改
import { AppDataSource } from './data-source'import { User } from './entity/User'
AppDataSource.initialize() .then(async () => { const user1 = new User() user1.name = 'jack' user1.nickname = '张三' user1.phone = '13311111111' user1.age = 25 user1.desc = 'This is a desc1'
// 插入数据 await AppDataSource.manager.save(user1) console.log('Saved a new user with id: ' + user1.id)
const user2 = new User() user2.name = 'rose' user2.nickname = '李四' user2.phone = '13322222222' user2.age = 26 user2.desc = 'This is a desc2' await AppDataSource.manager.save(user2) console.log('Saved a new user with id: ' + user2.id)
// 查询数据 const users = await AppDataSource.manager.find(User) console.log('查询所有用户信息: ', users)
// 更新数据 user1.name = 'tom' await AppDataSource.manager.save(user1)
// 查询数据 const user = await AppDataSource.manager.findOneBy(User, { id: 2, })
console.log('用户信息:', user) }) .catch((error) => console.log(error))使用Repository操作CRUD#
除了使用实体管理器(EntityManager)来管理实体外,我们还可以使用存储库(Repository)来管理每个实体,我们直接通过代码来看看
import { AppDataSource } from './data-source'import { User } from './entity/User'
async function main() { await AppDataSource.initialize() const userRepository = AppDataSource.getRepository(User)
const user1 = new User() user1.name = 'jack' user1.nickname = '张三' user1.phone = '13311111111' user1.age = 25 user1.desc = 'This is a desc1'
// 新增 user1 await userRepository.save(user1) console.log('新增成功...', user1)
const user2 = new User() user2.name = 'rose' user2.nickname = '李四' user2.phone = '13322222222' user2.age = 26 user2.desc = 'This is a desc2' // 新增 user2 await userRepository.save(user2) console.log('新增成功...', user2)
// 查询 const users = await userRepository.find() console.log('查询所有用户信息:', users)
// 根据条件查询 const user = await userRepository.findOneBy({ id: 2 }) console.log('查询id=2的用户信息:', user)
// 更新 user.name = 'tom' user.nickname = '王五' await userRepository.save(user) console.log('更新成功...', user)
// 删除 // const userDelete = await userRepository.findOneBy({ id: 2 }); // await userRepository.delete(userDelete); // console.log("删除成功...");}
main()使用QueryBuilder操作CRUD#
QueryBuilder是TypeORM中最强大的功能之一,它提供了更加底层的数据库查询操作,灵活度高,支持组合更复杂的SQL查询语句,比如多条件查询、多表查询等等。
创建QueryBuilder的方式有很多,可以通过DataSource创建,也可以通过Repository创建,还能通过EntityManager创建
// DataSource创建async function main() { await AppDataSource.initialize() const user = await AppDataSource.createQueryBuilder() .select('user') .from(User, 'user') .where('user.id = :id', { id: 1 }) .getOne() console.log('查询id=1的用户信息:', user)}main()// Repository创建const user = await AppDataSource.getRepository(User) .createQueryBuilder('user') .where('user.id = :id', { id: 1 }) .getOne()console.log('查询id=1的用户信息:', user)// EntityManager创建const user = await AppDataSource.manager .createQueryBuilder(User, 'user') .where('user.id = :id', { id: 1 }) .getOne()console.log('查询id=1的用户信息:', user)使用哪种方式创建都可以。
比如我们现在要新增一条数据
async function main() { await AppDataSource.initialize() const user = new User() user.name = 'lily' user.nickname = '丽丽' user.phone = '13333333333' user.age = 18 user.desc = '......'
const queryBuilder = AppDataSource.createQueryBuilder() const result = await queryBuilder.insert().into(User).values(user).execute() console.log('插入数据:', result)}
main()上面代码使用insert和into方法进行插入操作,语法上接近原始的SQL语句,value方法中可以传入对象或者数组,如果要插入多条记录,就可以选择传入数组。最后调用execute方法执行语句
如果要执行查询:
const saveUsers = await queryBuilder.select('u').from(User, 'u').getMany()console.log('查询所有用户信息:', saveUsers)同样查询操作类似于**“select from”的SQL语句,其实u是User**实体的别名,getMany方法可以把多条记录查询出来。注意查询出来的记录和我们代码的实体对象一一对应。
可能有时候我们数据库的字段和实体字段并不一致,如果仅仅就只想查询数据库中的字段,可以使用getRawMany,比如我们有这样的实体:
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
@Entity()export class User { @PrimaryGeneratedColumn() id: number
@Column({ name: 'u_name', length: 100, }) name: string
@Column({ name: 'u_nickname', length: 100, }) nickname: string
@Column() age: number
@Column({ length: 11 }) phone: string
@Column('text') desc: string
@Column('double', { default: 0, }) other: number}查询的时候
const saveUsers = await queryBuilder.select().from(User, 'u').getRawMany()console.log('查询所有用户信息:', saveUsers)就能获取数据库原始的字段。
如果我们想查询单条信息并修改
const userUpdate = await queryBuilder .select('u') .from(User, 'u') .where('u.id = :id', { id: 1 }) .getOne()console.log('查询id=1的用户信息:', userUpdate)
userUpdate.name = 'lucy'userUpdate.nickname = '露西'userUpdate.phone = '13344444444'userUpdate.age = 20userUpdate.desc = '123456'const resultUpdate = await queryBuilder .update(User) .set(userUpdate) .where('id = :id', { id: 1 }) .execute()console.log('更新数据:', resultUpdate)最后是删除数据
const resultDelete = await queryBuilder .delete() .from(User) .where('id = :id', { id: 1 }) .execute()console.log('删除数据:', resultDelete)三、TypeORM处理多表关系#
一对一关系#
以用户表和身份证表为例,用户表上面已经创建过了,接下来创建一个身份证实例
import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn,} from 'typeorm'import { User } from './User'
@Entity()export class IdCard { @PrimaryGeneratedColumn() id: number
@Column({ length: 18 }) cardNo: string
@Column() name: string
@Column() address: string
@Column() birthday: Date
@Column() email: string
@OneToOne(() => User) @JoinColumn() user: User}上面最关键的是,是 @OneToOne装饰器来建立与User实体之间的一对一关联关系。同时用 @JoinColumn()装饰器来定义user列,用来维护一个外键,TypeORM会自动帮我们生成外键id
**注意:**添加了实体,就需要将实体添加到DataSource配置选择entites中
export const AppDataSource = new DataSource({......entities: [User, IdCard],});当然,如果你配置的是
./**/entity/*.ts这样的通配符路径,则可以忽略
接下来,我们在index.ts中分别为User和IdCard插入数据,关键点是要确保他们关联起来
import { AppDataSource } from './data-source'import { IdCard } from './entity/IdCard'import { User } from './entity/User'
async function main() { await AppDataSource.initialize() // 创建一个用户 const user = new User() user.name = 'hmm' user.nickname = '韩梅梅' user.phone = '13355555555' user.age = 18 user.desc = '......'
// 创建一个身份证信息 const idCard = new IdCard() idCard.cardNo = '123456789012345678' idCard.name = 'hmm' idCard.address = '北京市朝阳区' idCard.birthday = new Date('2000-01-01') idCard.email = 'hmm2000@163.com' // 关联两个实体 idCard.user = user
// 获取实体存储库 const userRepository = AppDataSource.getRepository(User) const idCardRepository = AppDataSource.getRepository(IdCard)
// 先保存用户信息 await userRepository.save(user) console.log('保存用户信息成功...', user)
// 再保存身份证信息 await idCardRepository.save(idCard) console.log('保存身份证信息成功...', idCard)}
main()在数据库中,我们已经可以看到两张表的关联关系,比如查看id_card表的相关DDL信息,就能看到自动生成的建表信息

如果在数据库中,我们要查询两者信息,可以使用SQL语句
select * from user join id_card on user.id = id_card.userid
在代码中,我们可以使用Repository提供的find方法来查询。
但是两个实体的关系是单向的,也就是我们只在IdCard实体中持有User实体的外键列,这就使得IdCard有权访问User,但是反过来User其实并不知道IdCard的存在。
在这种情况下应该使用idCardRepository来查询,并且设置关联(relations)
async function main() { await AppDataSource.initialize()
// 获取idCard实体存储库 const idCardRepository = AppDataSource.getRepository(IdCard)
const idCards = await idCardRepository.find({ relations: { user: true }, })
console.log(idCards)}
main()双向关联#
所以,为了让User实体能够访问到IdCard实体,我们需要修改实体类建立双向关联
@Entity()export class IdCard { //...... 省略
@OneToOne(() => User, (user) => user.card) @JoinColumn() user: User}@Entity()export class User { // ...... 省略
@OneToOne(() => IdCard, (idCard) => idCard.user) card: IdCard}此时,如果我们用userRepository同样也就可以加载关联表数据了
import { AppDataSource } from './data-source'import { IdCard } from './entity/IdCard'import { User } from './entity/User'
async function main() { await AppDataSource.initialize()
// 获取idCard实体存储库 const idCardRepository = AppDataSource.getRepository(IdCard)
// 通过idCard实体查询关联表数据 const idCards = await idCardRepository.find({ relations: { user: true }, })
console.log('idCards---', idCards)
// 获取user实体存储库 const userRepository = AppDataSource.getRepository(User) const users = await userRepository.find({ relations: { card: true }, })
console.log('users---', users)}
main()级联处理#
前面我们保存User和IdCard其实是分别用了两个存储库去处理,但是这两个有关联的数据,我们完全可以进行级联更新或者删除。只需要在实体类中加上级联处理就行:
@Entity('user')export class User { // ......省略
@OneToOne(() => IdCard, (idCard) => idCard.user, { cascade: true, }) card: IdCard}还是之前的代码,但是现在我们只需要使用User库就能直接进行保存
async function main() { await AppDataSource.initialize() // 创建一个用户 const user = new User() user.name = 'lilei' user.nickname = '李雷' user.phone = '13366666666' user.age = 18 user.desc = '......'
// 创建一个身份证信息 const idCard = new IdCard() idCard.cardNo = '123456789012345678' idCard.name = 'lilei' idCard.address = '北京市朝阳区' idCard.birthday = new Date('2000-02-02') idCard.email = 'lilei2000@163.com'
// 注意这里需要通过user来关联idCard user.card = idCard
// 获取实体存储库 const userRepository = AppDataSource.getRepository(User)
// 直接保存用户信息即可关联保存idCard信息 await userRepository.save(user) console.log('保存用户信息成功...', user)}
main()那如果是更新呢?其实还是一样的。
async function main() { await AppDataSource.initialize() // 获取user实体存储库 const userRepository = AppDataSource.getRepository(User)
const loadUser = await userRepository.findOne({ relations: { card: true }, where: { id: 1 }, })
console.log('user:', loadUser)
loadUser.name = 'lucy' loadUser.nickname = '露西' loadUser.phone = '13377777777' loadUser.age = 20 loadUser.card.name = 'lucy' loadUser.card.cardNo = '999999999999999' loadUser.card.address = '成都市武侯区' loadUser.card.birthday = new Date('2000-03-03') loadUser.card.email = 'lucy2000@163.com'
const result = await userRepository.save(loadUser)
console.log(result)}
main()一对多/多对一关系#
一对多/多对一关系是我们最常见的关系,班级和学生,部门和员工,可以拿这些简单的内容来举例。比如部门可员工(一个部门可以有多个员工,一个员工只属于一个部门)
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm'import { Employee } from './Employee'
@Entity()export class Department { @PrimaryGeneratedColumn() id: number
@Column({ length: 100 }) name: string
@Column() desc: string
@OneToMany(() => Employee, (employee) => employee.department, { cascade: true, }) employees: Employee[]}使用@OneToMany装饰器来定义与员工表的关系,并设置级联(cascade)选项用来自动更新相关联的Employee记录。由于是多,所以employee肯定是一个数组。
不过@OneToMany并不能单独使用,它必须与@ManyToOne装饰器配对使用,以确保关系的双向性。
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn,} from 'typeorm'import { Department } from './Department'
@Entity()export class Employee { @PrimaryGeneratedColumn() id: number
@Column({ length: 100 }) name: string
@ManyToOne(() => Department, (department) => department.employees) @JoinColumn() department: Department}上面使用**@JoinColumn装饰器定义了外键列,在一对多/多对一关系中,外键列肯定会设置在”多”的一方**
实体定义完成之后,在index.ts中插入数据:
async function main() { await AppDataSource.initialize() const depart = new Department() depart.name = '技术部' depart.desc = '技术部门'
const emp1 = new Employee() emp1.name = '员工1' const emp2 = new Employee() emp2.name = '员工2'
// 设置部门和员工的关联关系 depart.employees = [emp1, emp2]
const departmentRepository = AppDataSource.getRepository(Department) const result = await departmentRepository.save(depart) console.log(result)}
main()多对多关系#
这里我们使用订单和商品的关系,一个订单可以包含多件商品,一件商品也可以出现在多个订单中。首先,当然还是需要先定义实体。
import { Entity, PrimaryGeneratedColumn, Column, ManyToMany } from 'typeorm'import { Product } from './Product'
@Entity()export class Order { @PrimaryGeneratedColumn() id: number
@Column({ length: 100 }) name: string
@ManyToMany(() => Product, (product) => product.orders, { cascade: true, }) products: Product[]}import { Entity, PrimaryGeneratedColumn, Column, ManyToMany, JoinTable,} from 'typeorm'import { Order } from './Order'
@Entity()export class Product { @PrimaryGeneratedColumn() id: number
@Column({ length: 100 }) name: string
@ManyToMany(() => Order, (order) => order.products) @JoinTable() orders: Order[]}这里使用@JoinTable()来生成中间表,TypeORM会创建一个中间表来管理多对多关系。
在index.ts中添加数据来看看效果
async function main() { await AppDataSource.initialize() const order1 = new Order() order1.name = '订单1' const order2 = new Order() order2.name = '订单2'
const product1 = new Product() product1.name = '产品1' const product2 = new Product() product2.name = '产品2' const product3 = new Product() product3.name = '产品3' const product4 = new Product() product4.name = '产品4'
order1.products = [product1, product2, product3] order2.products = [product1, product2, product3, product4]
const orderRepository = AppDataSource.getRepository(Order) await orderRepository.save(order1) await orderRepository.save(order2)}
main()上面的代码会自动帮我们生成一张中间表product_orders_order,而且生成了productId和orderId作为复合主键,并插入数据:


除此之外,我们还可以自行定义中间表的名称和列名
@Entity()export class Product { @PrimaryGeneratedColumn() id: number
@Column({ length: 100 }) name: string
@ManyToMany(() => Order, (order) => order.products) @JoinTable({ name: 'order_products', joinColumn: { name: 'order_id', referencedColumnName: 'id', }, inverseJoinColumn: { name: 'product_id', referencedColumnName: 'id', }, }) orders: Order[]}
四、在Nest中使用TypeORM#
掌握了TypeORM的基本用法之后,在Nest中集成TypeORM就特别简单了。
我们先重新创建一个nest工程
nest n nest-typeorm-mysql -p -pnpm为了方便,我们直接创建一个User模块
nest g res user然后导入我们需要的TypeORM相关的包
pnpm add typeorm mysql2 @nestjs/typeorm -sTypeORM提供了初始化数据库连接的方式,在app.module.ts文件中引入
@Module({ imports: [ TypeOrmModule.forRoot({ type: 'mysql', host: 'localhost', username: 'root', port: 3306, password: '123456', database: 'nest_typeorm', entities: [__dirname + '/**/*.entity{.ts,.js}'], synchronize: true, timezone: 'Z', }), UserModule, ], controllers: [AppController], providers: [AppService],})export class AppModule {}**注意:**这里是为了方便,打开了
synchronize: true,会自动加载或更新数据库结构匹配实体定义。但是在生产环境中,我们肯定要禁用同步,不然随便就改变了数据库结构这是非常不安全的。由于docker中mysql数据库我们并没有处理时区问题,这样存储进去的时间类型,反馈给前端的时候就会出现东8区的时间差问题。
上面的代码通过forRoot方法在根模块(AppModule)中注册了TypeORM模块。这样可以使得TypeORM服务在应用程序的各个模块中都可以共享使用,不过需要注意,这里需要node版本在20以上。
完善一下User实体,新增一些字段
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
@Entity()export class User { @PrimaryGeneratedColumn() id: number
@Column() name: string
@Column() sex: string
@Column({ type: 'timestamp', transformer: { to: (value: string | Date): Date => { if (typeof value === 'string') { return new Date(`${value}T00:00:00.000Z`) } return value }, from: (value: Date): Date => { return value }, }, }) birthday: Date}使用命令pnpm run start:dev启动服务,就能帮我们创建User表(注意:数据库需要自己手动创建)

EntityManager操作实体#
我们可以通过nest直接注入EntityManager来操作实体,这里唯一需要注意的一点是,TypeORM是天生的Repository,所以,我们可以直接在Service层中注入EntityManager
user.service.ts
import { Injectable } from '@nestjs/common'import { CreateUserDto } from './dto/create-user.dto'import { UpdateUserDto } from './dto/update-user.dto'import { InjectEntityManager } from '@nestjs/typeorm'import { EntityManager } from 'typeorm'import { User } from './entities/user.entity'
@Injectable()export class UserService { @InjectEntityManager() private entityManager: EntityManager
async create(createUserDto: CreateUserDto) { const user = await this.entityManager.save(User, createUserDto) console.log(user) return user }
async findAll() { return await this.entityManager.find(User) }
async findOne(id: number) { return await this.entityManager.findOneBy(User, { id }) }
async update(id: number, updateUserDto: UpdateUserDto) { return await this.entityManager.save(User, { id, ...updateUserDto }) }
async remove(id: number) { return await this.entityManager.delete(User, { id }) }}user.controller.ts
import { Controller, Get, Post, Body, Patch, Param, Delete,} from '@nestjs/common'import { UserService } from './user.service'import { CreateUserDto } from './dto/create-user.dto'import { UpdateUserDto } from './dto/update-user.dto'
@Controller('user')export class UserController { constructor(private readonly userService: UserService) {}
@Post() create(@Body() createUserDto: CreateUserDto) { return this.userService.create(createUserDto) }
@Get() findAll() { return this.userService.findAll() }
@Get(':id') findOne(@Param('id') id: string) { return this.userService.findOne(+id) }
@Patch(':id') update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) { return this.userService.update(+id, updateUserDto) }
@Delete(':id') remove(@Param('id') id: string) { return this.userService.remove(+id) }}Repository操作实体#
除了使用EntityManager操作实体之外,同样可以使用Repository操作实体,其实在功能和使用上和EntityManager几乎一致,唯一的区别是,使用Repository不需要每次操作都传入实体对象
使用EntityManager的小问题:

我们注入Repository,替换一下就行:
import { Injectable } from '@nestjs/common'import { CreateUserDto } from './dto/create-user.dto'import { UpdateUserDto } from './dto/update-user.dto'import { InjectRepository } from '@nestjs/typeorm'import { Repository } from 'typeorm'import { User } from './entities/user.entity'
@Injectable()export class UserService { @InjectRepository(User) private userRepository: Repository<User>
async create(createUserDto: CreateUserDto) { const user = await this.userRepository.save(createUserDto) return user }
async findAll() { return await this.userRepository.find() }
async findOne(id: number) { return await this.userRepository.findOneBy({ id }) }
async update(id: number, updateUserDto: UpdateUserDto) { return await this.userRepository.save({ id, ...updateUserDto }) }
async remove(id: number) { return await this.userRepository.delete({ id }) }}但是要注意的是:由于Nest的依赖注入作用域限定在模块级别,因此userRepository存储库还需要在User模块中引入并注册存储库,所以,在user.module.ts文件中,还需要添加如下的代码:
@Module({ imports: [TypeOrmModule.forFeature([User])], controllers: [UserController], providers: [UserService],})export class UserModule {}QueryBuilder操作实体#
当然,如果我们有比较复杂的SQL语句,还能使用QueryBuilder对实体进行操作。比如有下面的查询,需要根据姓名和性别进行查询,如果使用Repository我们可能会写成下面这个样子
async create(createUserDto: CreateUserDto) { const user = await this.userRepository.save(createUserDto); return user;}async findAll(name: string, sex: string) { return await this.userRepository.find({ where: { name, sex, }, });}为了查询方便,controller中稍微做一下修改:
@Post() create(@Body() createUserDto: CreateUserDto) { return this.userService.create(createUserDto); }
@Get() findAll(@Query('name') name: string, @Query('sex') sex: string) { return this.userService.findAll(name, sex); }如果我们已经有下面的数据

经过上面的查询,实际上where中的连接条件是and
查询之后的结果如下:

如果我们希望使用or进行连接,就可以使用QueryBuilder
async create(createUserDto: CreateUserDto) { // const user = await this.userRepository.save(createUserDto); const user = await this.userRepository .createQueryBuilder() .insert() .into(User) .values(createUserDto) .execute(); return user;}
async findAll(name: string, sex: string) { // return await this.userRepository.find({ // where: { // name, // sex, // }, // });
return this.userRepository .createQueryBuilder('u') .where('u.name = :name or u.sex = :sex', { name, sex }) .getMany();}查询出来的结果:
