Skip to content

在 nest 中使用 typeorm

typeorm 基础使用

初始化一个 typeorm 项目

bash
npx typeorm init --name typeorm_learn --database mysql
  • --name: 项目名称
  • --database: 数据库类型

执行以上命令后,会在当前目录下生成一个 typeorm_learn 的文件夹,里面包含了一个 typeorm 项目的基本结构。一般是使用 mysql2 来连接 mysql 数据库,所以需要安装 mysql2 依赖。

bash
pnpm i mysql2

配置数据库连接

ts
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

ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number

  @Column()
  firstName: string

  @Column()
  lastName: string

  @Column()
  age: number
}

启动下项目,会发现数据库中多了一个 user 表,表中有 idfirstNamelastNameage 四个字段。而且控制台还打印出了生成的 sql 语句。

sql语句

user表

通过控制台和数据库表可以看出,typeorm 会根据实体类来创建表,而且会根据实体类中的字段来创建表中的字段。然后会有默认的数据类型映射到数据库中,比如 firstNamelastNameage 都是 string 类型,但是在数据库中都是 varchar 类型。但是有些时候想要自定义类型,并不是使用这样的默认类型,这时候就需要使用装饰器来自定义类型。

ts
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
}

TIP

  • @Entity: 标识这是一个实体类
  • @PrimaryGeneratedColumn: 标识这是一个主键,并且是自增的
  • @Column: 标识这是一个字段,并且可以自定义字段的类型、长度、名称、注释等
  • @Colum 中可以自定义字段的类型,但是需要注意的是,如果是 mysql 数据库,那么 type 的值需要是 mysql 数据库中的数据类型,比如 varcharintdoubletext 等。按照这个实体重新生成 user

user表

user表

通过对实体的定义就可以新建表以及确定数据的类型。

对数据库表进行增删改查

新增和修改

  1. save 方法

index.ts 进行测试添加

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,那么会更新数据

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.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就行了

ts
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))

批量插入数据

  1. insert 方法
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 = '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))

insert

save的区别是不会先查一遍表,而是直接插入数据。

  1. update 方法
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 = '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))

update

删除数据

  1. remove 方法
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.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))

remove

  1. delete 方法
ts
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就是单个删除,如果传递的是个数组就是批量删除

delete

removedelete的区别是remove会先查询一遍表,然后再删除,而delete是直接删除。

查询数据

  1. find 方法
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 users = await AppDataSource.manager.find(User)
    console.log(users)
  })
  .catch((error) => console.log(error))

find

也可以添加where条件

ts
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))
  1. findOne 方法
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 = await AppDataSource.manager.findOne(User, {
      select: {
        firstName: true,
        age: true
      },
      where: {
        id: 7
      },
      order: {
        age: 'ASC'
      }
    })
    console.log(user)
  })
  .catch((error) => console.log(error))

findOne

  1. findBy 方法
ts
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))

findBy

  1. findAndCount 方法
ts
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))

findAndCount

也可以添加where条件

ts
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))
  1. findOneBy 方法
ts
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))

findOneBy

  1. findOneOrFailfindOneByOrFail 方法
ts
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))

findOneOrFail

如果没有查询到数据,会抛出异常

query

有时候可能想直接执行一个简单的sql语句,那么可以使用query方法

ts
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

query builder

query builder 是一个链式调用的方式来构建sql语句,可以使用它来构建复杂的sql语句。

ts
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))

queryBuilder

事务 transaction

ts
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))

这样就开启了事务

transaction 这里只是模拟下开启,实际上没有做任何操作,所以没有打印出任何数据。

getRepository

在上面的方法中,每次的操作都会在manager上传递实体,如果想要简化操作,可以使用getRepository方法,拿到这个实体的repository,然后就可以直接使用这个repository来操作数据库了。

ts
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的基本使用,包括了数据库的连接、实体类的创建、实体类的字段类型、实体类的增删改查、事务、queryquery buildergetRepository。确实总体就是这些方法,用的时候只需要根据自己的需求来选择合适的方法就行了。

TIP

  • 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 项目,然后引入需要使用的依赖

bash
pnpm add @nestjs/typeorm typeorm mysql2

然后在app.module.ts中引入typeorm模块

ts
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的方法来操作数据库了。

ts
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中的方法了。

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)
  }
}

这样就可以在nest中使用typeorm了。这时候已经可以做增删改查的业务了,但是有一个小的地方可以优化下,就是在service中每次都需要注入manager,这样会比较麻烦,所以可以使用@InjectRepository来注入repository,然后就可以直接使用repository来操作数据库了。

ts
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<User>
  ) {}

  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 实体类

ts
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的使用还是比较简单的,只需要根据自己的需求来选择合适的方法就行了。希望对你有帮助。

如有转载或 CV 的请标注本站原文地址