在 nest 中使用 typeorm
typeorm
基础使用
初始化一个 typeorm
项目
npx typeorm init --name typeorm_learn --database mysql
- --name: 项目名称
- --database: 数据库类型
执行以上命令后,会在当前目录下生成一个 typeorm_learn
的文件夹,里面包含了一个 typeorm
项目的基本结构。一般是使用 mysql2
来连接 mysql
数据库,所以需要安装 mysql2
依赖。
pnpm i mysql2
配置数据库连接
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_learn',
// 同步数据库
synchronize: true,
// 开启打印生成sql语句
logging: false,
// 实体类
entities: ['./**/entity/*.ts'],
migrations: [],
// 订阅
subscribers: [],
// 连接池
connectorPackage: 'mysql2'
})
创建实体类
数据库中的表是根据实体类来创建的,所以需要创建实体类。看下生成的默认实体类 User.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column()
firstName: string
@Column()
lastName: string
@Column()
age: number
}
启动下项目,会发现数据库中多了一个 user
表,表中有 id
、firstName
、lastName
、age
四个字段。而且控制台还打印出了生成的 sql
语句。
通过控制台和数据库表可以看出,typeorm
会根据实体类来创建表,而且会根据实体类中的字段来创建表中的字段。然后会有默认的数据类型映射到数据库中,比如 firstName
、lastName
、age
都是 string
类型,但是在数据库中都是 varchar
类型。但是有些时候想要自定义类型,并不是使用这样的默认类型,这时候就需要使用装饰器来自定义类型。
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column({
type: 'varchar',
length: 20,
name: 'first_name',
comment: '姓氏'
})
firstName: string
@Column({
type: 'varchar',
name: 'last_name',
length: 20,
comment: '名字'
})
lastName: string
@Column({
type: 'int',
comment: '年龄'
})
age: number
@Column({
type: 'double',
comment: 'num'
})
num: number
@Column({
type: 'text',
comment: 'text'
})
text: string
}
- @Entity: 标识这是一个实体类
- @PrimaryGeneratedColumn: 标识这是一个主键,并且是自增的
- @Column: 标识这是一个字段,并且可以自定义字段的类型、长度、名称、注释等
- 在
@Colum
中可以自定义字段的类型,但是需要注意的是,如果是mysql
数据库,那么type
的值需要是mysql
数据库中的数据类型,比如varchar
、int
、double
、text
等。按照这个实体重新生成user
表
通过对实体的定义就可以新建表以及确定数据的类型。
对数据库表进行增删改查
新增和修改
save
方法在 index.ts
进行测试添加
import { AppDataSource } from './data-source'
import { User } from './entity/User'
AppDataSource.initialize()
.then(async () => {
console.log('Inserting a new user into the database...')
const user = new User()
user.firstName = 'Timber'
user.lastName = 'Saw'
user.age = 25
user.count = 1.1
user.text = 'text'
await AppDataSource.manager.save(user)
console.log('Saved a new user with id: ' + user.id)
console.log('Loading users from the database...')
const users = await AppDataSource.manager.find(User)
console.log('Loaded users: ', users)
console.log('Here you can setup and run express / fastify / any other framework.')
})
.catch((error) => console.log(error))
在命令行重新启动下项目,可以看到控制台打印出了新增的 user
数据
如果在save
的时候指定了id
,那么会更新数据
import { AppDataSource } from './data-source'
import { User } from './entity/User'
AppDataSource.initialize()
.then(async () => {
console.log('Inserting a new user into the database...')
const user = new User()
user.id = 1
user.firstName = 'water'
user.lastName = 'Saw'
user.age = 25
user.count = 1.1
user.text = 'text'
await AppDataSource.manager.save(user)
console.log('Saved a new user with id: ' + user.id)
console.log('Loading users from the database...')
const users = await AppDataSource.manager.find(User)
console.log('Loaded users: ', users)
console.log('Here you can setup and run express / fastify / any other framework.')
})
.catch((error) => console.log(error))
在命令行重新启动下项目,可以看到控制台打印出了更新的 user
数据
如果想要批量插入或者修改数据,可以使用 save
方法,修改的话和上面的一样,传入id
就行了
import { AppDataSource } from './data-source'
import { User } from './entity/User'
AppDataSource.initialize()
.then(async () => {
await AppDataSource.manager.save(User, [
{
firstName: 'water1',
lastName: 'Saw',
age: 25,
count: 1.1,
text: 'text'
},
{
firstName: 'water2',
lastName: 'Saw',
age: 18,
count: 1.1,
text: 'text11'
}
])
})
.catch((error) => console.log(error))
import { AppDataSource } from './data-source'
import { User } from './entity/User'
AppDataSource.initialize()
.then(async () => {
console.log('Inserting a new user into the database...')
const user = new User()
user.firstName = 'water'
user.lastName = 'Saw'
user.age = 25
user.count = 1.1
user.text = 'text'
await AppDataSource.manager.insert(User, user)
console.log('Saved a new user with id: ' + user.id)
console.log('Loading users from the database...')
const users = await AppDataSource.manager.find(User)
console.log('Loaded users: ', users)
console.log('Here you can setup and run express / fastify / any other framework.')
})
.catch((error) => console.log(error))
与save
的区别是不会先查一遍表,而是直接插入数据。
import { AppDataSource } from './data-source'
import { User } from './entity/User'
AppDataSource.initialize()
.then(async () => {
console.log('Inserting a new user into the database...')
const user = new User()
user.firstName = 'water22'
user.lastName = 'Saw'
user.age = 25
user.count = 1.1
user.text = 'text'
await AppDataSource.manager.update(User, 1, user)
console.log('Saved a new user with id: ' + user.id)
console.log('Loading users from the database...')
const users = await AppDataSource.manager.find(User)
console.log('Loaded users: ', users)
console.log('Here you can setup and run express / fastify / any other framework.')
})
.catch((error) => console.log(error))
删除数据
remove
方法import { AppDataSource } from './data-source'
import { User } from './entity/User'
AppDataSource.initialize()
.then(async () => {
console.log('Inserting a new user into the database...')
const user = new User()
user.id = 1
await AppDataSource.manager.remove(User, user)
console.log('Saved a new user with id: ' + user.id)
console.log('Loading users from the database...')
const users = await AppDataSource.manager.find(User)
console.log('Loaded users: ', users)
console.log('Here you can setup and run express / fastify / any other framework.')
})
.catch((error) => console.log(error))
delete
方法import { AppDataSource } from './data-source'
import { User } from './entity/User'
AppDataSource.initialize()
.then(async () => {
console.log('Inserting a new user into the database...')
await AppDataSource.manager.delete(User, 2)
await AppDataSource.manager.delete(User, [3, 4])
})
.catch((error) => console.log(error))
如果传递的是一个id
就是单个删除,如果传递的是个数组就是批量删除
remove
和delete
的区别是remove
会先查询一遍表,然后再删除,而delete
是直接删除。
查询数据
import { AppDataSource } from './data-source'
import { User } from './entity/User'
AppDataSource.initialize()
.then(async () => {
console.log('Inserting a new user into the database...')
const users = await AppDataSource.manager.find(User)
console.log(users)
})
.catch((error) => console.log(error))
也可以添加where
条件
import { AppDataSource } from './data-source'
import { User } from './entity/User'
AppDataSource.initialize()
.then(async () => {
const users = await AppDataSource.manager.find(User, {
where: {
age: 18
}
})
console.log(users)
})
.catch((error) => console.log(error))
import { AppDataSource } from './data-source'
import { User } from './entity/User'
AppDataSource.initialize()
.then(async () => {
console.log('Inserting a new user into the database...')
const user = await AppDataSource.manager.findOne(User, {
select: {
firstName: true,
age: true
},
where: {
id: 7
},
order: {
age: 'ASC'
}
})
console.log(user)
})
.catch((error) => console.log(error))
import { AppDataSource } from './data-source'
import { User } from './entity/User'
AppDataSource.initialize()
.then(async () => {
const users = await AppDataSource.manager.findBy(User, {
age: 18
})
console.log(users)
})
.catch((error) => console.log(error))
import { AppDataSource } from './data-source'
import { User } from './entity/User'
AppDataSource.initialize()
.then(async () => {
const [users, count] = await AppDataSource.manager.findAndCount(User)
console.log(users, count)
})
.catch((error) => console.log(error))
也可以添加where
条件
import { AppDataSource } from './data-source'
import { User } from './entity/User'
AppDataSource.initialize()
.then(async () => {
const [users, count] = await AppDataSource.manager.findAndCount(User, {
where: {
age: 18
}
})
console.log(users, count)
})
.catch((error) => console.log(error))
findOneBy
方法import { AppDataSource } from './data-source'
import { User } from './entity/User'
AppDataSource.initialize()
.then(async () => {
const user = await AppDataSource.manager.findOneBy(User, {
age: 18
})
console.log(user)
})
.catch((error) => console.log(error))
findOneOrFail
和 findOneByOrFail
方法import { AppDataSource } from './data-source'
import { User } from './entity/User'
AppDataSource.initialize()
.then(async () => {
const user = await AppDataSource.manager.findOneOrFail(User, { where: { id: 1 } })
console.log(user)
})
.catch((error) => console.log(error))
如果没有查询到数据,会抛出异常
query
有时候可能想直接执行一个简单的sql
语句,那么可以使用query
方法
import { AppDataSource } from './data-source'
AppDataSource.initialize()
.then(async () => {
const users = await AppDataSource.manager.query(
'select * from user where age in(?, ?)',
[18, 30]
)
console.log(users)
})
.catch((error) => console.log(error))
query builder
query builder
是一个链式调用的方式来构建sql
语句,可以使用它来构建复杂的sql
语句。
import { AppDataSource } from './data-source'
import { User } from './entity/User'
AppDataSource.initialize()
.then(async () => {
const queryBuilder = await AppDataSource.manager.createQueryBuilder()
const user = await queryBuilder
.select('user')
.from(User, 'user')
.where('user.age = :age', { age: 18 })
.getOne()
console.log(user)
})
.catch((error) => console.log(error))
事务 transaction
import { AppDataSource } from './data-source'
import { User } from './entity/User'
AppDataSource.initialize()
.then(async () => {
await AppDataSource.manager.transaction(async (manager) => {
await manager.save(User, {
id: 4,
firstName: 'water55',
lastName: 'ice',
age: 20
})
})
})
.catch((error) => console.log(error))
这样就开启了事务
这里只是模拟下开启,实际上没有做任何操作,所以没有打印出任何数据。
getRepository
在上面的方法中,每次的操作都会在manager
上传递实体,如果想要简化操作,可以使用getRepository
方法,拿到这个实体的repository
,然后就可以直接使用这个repository
来操作数据库了。
import { AppDataSource } from './data-source'
import { User } from './entity/User'
AppDataSource.initialize()
.then(async () => {
const userRepository = AppDataSource.manager.getRepository(User)
const user = await userRepository.findOne(1)
console.log(user)
})
.catch((error) => console.log(error))
所有的方法用法同之前的直接调用,只是这里改成了实体的repository
来调用。
上面介绍了typeorm
的基本使用,包括了数据库的连接、实体类的创建、实体类的字段类型、实体类的增删改查、事务、query
、query builder
、getRepository
。确实总体就是这些方法,用的时候只需要根据自己的需求来选择合适的方法就行了。
- save:新增或者修改 Entity,如果传入了 id 会先 select 再决定修改还新增
- update:直接修改 Entity,不会先 select
- insert:直接插入 Entity
- delete:删除 Entity,通过 id
- remove:删除 Entity,通过对象
- find:查找多条记录,可以指定 where、order by 等条件
- findBy:查找多条记录,第二个参数直接指定 where 条件,更简便一点
- findAndCount:查找多条记录,并返回总数量
- findByAndCount:根据条件查找多条记录,并返回总数量
- findOne:查找单条记录,可以指定 where、order by 等条件
- findOneBy:查找单条记录,第二个参数直接指定 where 条件,更简便一点
- findOneOrFail:查找失败会抛 EntityNotFoundError 的异常
- query:直接执行 sql 语句
- createQueryBuilder:创建复杂 sql 语句,比如 join 多个 Entity 的查询
- transaction:包裹一层事务的 sql
- getRepository:拿到对单个 Entity 操作的类,方法同 EntityManager
nest
中接入并使用typeorm
新建一个 nest 项目,然后引入需要使用的依赖
pnpm add @nestjs/typeorm typeorm mysql2
然后在app.module.ts
中引入typeorm
模块
import { Module } from '@nestjs/common'
import { AppController } from './app.controller'
import { AppService } from './app.service'
import { TypeOrmModule } from '@nestjs/typeorm'
@Module({
imports: [
UserModule,
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: '123456',
database: 'typeorm_test',
synchronize: true,
logging: true,
entities: ['./**/entity/*.ts'],
connectorPackage: 'mysql2'
})
],
controllers: [AppController],
providers: [AppService]
})
export class AppModule {}
然后在就是定义实体,这个和单独用 typeorm 没什么区别,这里就不再赘述了。
一般操作数据库都是在service
中,所以在user.service.ts
中引入typeorm
,然后就可以使用typeorm
的方法来操作数据库了。
import { Injectable } from '@nestjs/common'
import { InjectEntityManager } from '@nestjs/typeorm'
import { EntityManager } from 'typeorm'
import { CreateUserDto } from './dto/create-user.dto'
import { UpdateUserDto } from './dto/update-user.dto'
import { User } from './entities/user.entity'
@Injectable()
export class UserService {
@InjectEntityManager()
private manager: EntityManager
create(createUserDto: CreateUserDto) {
this.manager.save(User, createUserDto)
}
findAll() {
return this.manager.find(User)
}
findOne(id: number) {
return this.manager.findOne(User, {
where: { id }
})
}
update(id: number, updateUserDto: UpdateUserDto) {
this.manager.save(User, {
id: id,
...updateUserDto
})
}
remove(id: number) {
this.manager.delete(User, id)
}
}
然后就是在controller
中调用service
中的方法了。
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)
}
}
这样就可以在nest
中使用typeorm
了。这时候已经可以做增删改查的业务了,但是有一个小的地方可以优化下,就是在service
中每次都需要注入manager
,这样会比较麻烦,所以可以使用@InjectRepository
来注入repository
,然后就可以直接使用repository
来操作数据库了。
import { Injectable } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { Repository } from 'typeorm'
import { CreateUserDto } from './dto/create-user.dto'
import { UpdateUserDto } from './dto/update-user.dto'
import { User } from './entities/user.entity'
@Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private userRepository: Repository
) {}
create(createUserDto: CreateUserDto) {
this.userRepository.save(createUserDto)
}
findAll() {
return this.userRepository.find()
}
findOne(id: number) {
return this.userRepository.findOne(id)
}
update(id: number, updateUserDto: UpdateUserDto) {
this.userRepository.save({
id: id,
...updateUserDto
})
}
remove(id: number) {
this.userRepository.delete(id)
}
}
这样就可以直接使用repository
来操作数据库了。还需要在当前模块引入 TypeOrmModule.forFeature 对应的动态模块 user 实体类
import { Module } from '@nestjs/common'
import { UserService } from './user.service'
import { UserController } from './user.controller'
import { TypeOrmModule } from '@nestjs/typeorm'
import { User } from './entities/user.entity'
@Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [UserController],
providers: [UserService]
})
export class UserModule {}
这样就可以使用repository
来操作数据库了。会简洁一些
小结
本文就简单介绍了typeorm
的基本使用,以及在nest
中使用typeorm
的方法。typeorm
的使用还是比较简单的,只需要根据自己的需求来选择合适的方法就行了。希望对你有帮助。