TypeORM在Nest里的应用

176 阅读9分钟

TypeORM初级

本章先不讲解在Nest框架里面使用TypeORM连接数据库

  • 先讲解TypeORM的基本用法
  • 初级部分讲解基本使用
  • 高级部分讲解表联查,一对一,一对多,多对多
  • 最后在Nest里实现一个小Deom

什么是ORM?

  • ORM 是 Object Relational Mapping,对象关系映射。
  • 关系型数据库的表映射成面向对象的 class,
  • 表的字段映射成对象的属性映射,
  • 表与表的关联映射成属性的关联。

初体验

创建连接

安装命令

npx typeorm@latest init --name typeorm-all-feature --database mysql
  • 通过 typeorm 的 init 命令创建一个项目, --name 指定项目名,--database 指定连接的数据库。
  • 我的本地数据库密码是 root root123

接下来修改data-source.ts这个文件

//data-source.tsimport "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: "root123",  // 数据库密码
    database: "test",  // 数据库名
    synchronize: true,  // 自动同步实体
    logging: true,  // 开启日志
    entities: [User], // 实体
    migrations: [],  // 迁移
    subscribers: [],  // 订阅
    connectorPackage: 'mysql2',  // 数据库驱动
    extra: {  // 额外配置
        authPlugin: 'sha256_password',  // 密码加密方式
    }
})

安装下 mysql2 这个驱动包

npm install --save mysql2

ok了配置好了 接下来启动一下

npm run start

image.png image.png 完美

数据表哪里来的呢?

  • 我们的目录entity下面有一个默认的User.ts文件

    • import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"@Entity() // 标明这是一个实体
      export class User {
      ​
          @PrimaryGeneratedColumn()  // 标明这是一个主键
          id: number
      ​
          @Column()  // 标明这是一个列
          firstName: string
      ​
          @Column()
          lastName: string
      ​
          @Column()
          age: number
      ​
      }
      ​
      
    • 通过装饰器声明了主键列和其他的列

初始数据哪里来的呢?

  • index.ts文件

    • AppDataSource.initialize().then(async () => {
          const user = new User()  // 创建实体
          user.firstName = "Timber"  // 赋值
          user.lastName = "Saw"  // 赋值
          user.age = 25  // 赋值
          await AppDataSource.manager.save(user)  // 保存
          const users = await AppDataSource.manager.find(User) // 查询
      }).catch(error => console.log(error))
      ​
      
    • 在 index.ts 里创建了一个 User 的对象,调用 save 方法保存这个对象,通过 find 方法查询这个对象:

创建表

我们创建一个表,也就是实体(Entity)试一下

比如我在创建一张商品表(good)

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"@Entity({
  name: "good" // 表名
})
export class Good {
  @PrimaryGeneratedColumn({
    type: "int",  // 类型
    name: "id",  // 字段名
    comment: "这是主键" // 备注
  })
  id: number;
​
  @Column({
    unique: true,  //唯一
    type: "varchar",  //数据库类型
    length: 20,  //长度
    comment: "商品名字"
  })
  name: string;
​
  @Column({
    name: "good_type",  // 字段名
    nullable: false,  //不能是空
    length: 10,
    type: 'varchar',
    default: 'bbb'  //默认值
  })
  type: string
​
  @Column({
    name: "price",  // 字段名
    nullable: false,  //不能是空
    length: 10,
    default: '100',  //默认值
  })
  price: string
​
​
}
  • 我们新增了一个 Entity Good
  • @Entity 指定它是一个 Entity,name 指定表名为 good
  • @PrimaryGeneratedColumn 指定它是一个自增的主键,通过 comment 指定注释
  • @Column 映射属性和字段的对应关系
  • 通过 name 指定字段名
  • type 指定映射的类型
  • length 指定长度
  • default 指定默认值
  • nullable 设置 NOT NULL 约束
  • unique 设置 UNIQUE 唯一索引
  • type 这里指定的都是数据库里的数据类型

接下来在 DataSource 的 entities 里引入下

import "reflect-metadata"
import { DataSource } from "typeorm"
import { User } from "./entity/User"
import { Good } from "./entity/Good"  //1.导入export const AppDataSource = new DataSource({
    type: "mysql",
    host: "localhost",
    port: 3306,
    username: "root",
    password: "root123",
    database: "test",
    synchronize: true,
    logging: true,
    entities: [User, Good],  //2.注入
    migrations: [],
    subscribers: [],
    connectorPackage: 'mysql2',
    extra: {
        authPlugin: 'sha256_password',
    }
})
​

OK了

npm run start

我们看一下数据库

image.png image.png 很完美,这样就成功创建出表了

增删改查(CRUD)

save(增加|修改)

  • 如果指定了id就是修改

    • 指定了 id 的时候,typeorm 会先查询这个 id 的记录,如果查到了,那就执行 update

    • 其实 EntityManager 还有 update 和 insert 方法,分别是修改和插入的,

      • 区别:它们不会先 select 查询一次。而 save 方法会先查询一次数据库来确定是插入还是修改。
  • 没有id当然就是添加了

//没有指定id
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"AppDataSource.initialize().then(async () => {
    const good = new Good()
    good.name = "Apple"
    good.type = "手机产品"
    good.price = "10"
​
    await AppDataSource.manager.save(good)
}).catch(error => console.log(error))

image.png

npm run start   //执行一下
//指定id
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"AppDataSource.initialize().then(async () => {
    const good = new Good()
    good.id = 1
    good.name = "Apple 修改之后"
    good.type = "手机产品"
    good.price = "10000"
​
    await AppDataSource.manager.save(good)
}).catch(error => console.log(error))
npm run start   //执行一下

image.png

save(批量添加和修改)

  • 和一个没什么区别
//没有id的  添加
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"AppDataSource.initialize().then(async () => {
​
    await AppDataSource.manager.save(Good, [
        { name: 'ccc', type: 'ccc', price: "21" },
        { name: 'ddd', type: 'ddd', price: "22" },
        { name: 'eee', type: 'eee', price: "23" }
    ])
}).catch(error => console.log(error))
​
​
//有id的  修改
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"AppDataSource.initialize().then(async () => {
​
    await AppDataSource.manager.save(Good, [
        { id: 2, name: 'ccc修改', type: 'ccc修改', price: "200001" },
        { id: 3, name: 'ddd修改', type: 'ddd', price: "200002" },
        { id: 4, name: 'eee', type: 'eee', price: "23" }
    ])
}).catch(error => console.log(error))

OK了

delete (删除)

单条和批量删除无非就是穿值和数组

  • 单条删除
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"AppDataSource.initialize().then(async () => {
​
    await AppDataSource.manager.delete(Good, 1);
}).catch(error => console.log(error))

image.png

  • 批量删除
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"AppDataSource.initialize().then(async () => {
​
    await AppDataSource.manager.delete(Good, [3, 4]);
}).catch(error => console.log(error))

image.png 大概就是这样 记得每次写完执行

npm run start   //执行一下

remove (删除)

image.png

  • 单条删除
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"AppDataSource.initialize().then(async () => {
    const good = new Good()
    good.id = 2
​
    await AppDataSource.manager.remove(Good, good)
}).catch(error => console.log(error))

image.png

两个删除的区别

delete 和 remove 的区别是,delete 直接传 id、而 remove 则是传入 entity 对象

查询

  • 查询东西就比较多了

    • find 查询数据

    • findBy 根据条件查询

    • findAndCount 多少条记录

    • findOne 查询一条

    • findOneBy

      • findOneOrFail
      • findOneByOrFail
    • query 直接写sql语句

    • createQueryBuilder 写sql语句 简化版 不会sql不熟练也没关系

    • transaction:包裹一层事务的 sql

    • getRepository:拿到对单个 Entity 操作的类,方法同 EntityManager

欧克现在我们试试吧

先插入一些数据

import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"AppDataSource.initialize().then(async () => {
​
    await AppDataSource.manager.save(Good, [
        { name: '小米', type: '手机', price: "1000" },
        { name: '华为', type: '手机', price: "5000" },
        { name: '红米', type: '手机', price: "2000" },
        { name: 'OPPO', type: '手机', price: "1500" },
        { name: '华硕', type: '电脑', price: "2200" },
        { name: '小米1', type: '电脑', price: "2000" },
        { name: '苹果1', type: '电脑', price: "99999" },
        { name: '小米2', type: '平板', price: "4999" },
        { name: '华为2', type: '平板', price: "399" },
    ])
}).catch(error => console.log(error))

image.png

find

import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"AppDataSource.initialize().then(async () => {
​
    const Goods = await AppDataSource.manager.find(Good);
    console.log(Goods);
}).catch(error => console.log(error))

看一下打印的数据

[
  Good { id: 16, name: '小米', type: '手机', price: '1000' },
  Good { id: 17, name: '华为', type: '手机', price: '5000' },
  Good { id: 18, name: '红米', type: '手机', price: '2000' },
  Good { id: 19, name: 'OPPO', type: '手机', price: '1500' },
  Good { id: 20, name: '华硕', type: '电脑', price: '2200' },
  Good { id: 21, name: '小米1', type: '电脑', price: '2000' },
  Good { id: 22, name: '苹果1', type: '电脑', price: '99999' },
  Good { id: 23, name: '小米2', type: '平板', price: '4999' },
  Good { id: 24, name: '华为2', type: '平板', price: '399' }
]

可以看到,打印的是数组对象

findBy(条件查询)

//名字等于小米的
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"AppDataSource.initialize().then(async () => {
​
    const Goods = await AppDataSource.manager.findBy(Good, {
        name: "小米"
    });
    console.log(Goods);
}).catch(error => console.log(error))
​
//打印的数据
[ Good { id: 16, name: '小米', type: '手机', price: '1000' } ]

findAndCount (多少条记录)

  • 可以解构出来 第一个就是数据了 第二个 就是条数
//不仅可以获得数据,也可以获取到条数
AppDataSource.initialize().then(async () => {
​
    const [goods, count] = await AppDataSource.manager.findAndCount(Good);
    console.log(goods, count);
}).catch(error => console.log(error))
​
//打印的数据
[
  Good { id: 16, name: '小米', type: '手机', price: '1000' },
  Good { id: 17, name: '华为', type: '手机', price: '5000' },
  Good { id: 18, name: '红米', type: '手机', price: '2000' },
  Good { id: 19, name: 'OPPO', type: '手机', price: '1500' },
  Good { id: 20, name: '华硕', type: '电脑', price: '2200' },
  Good { id: 21, name: '小米1', type: '电脑', price: '2000' },
  Good { id: 22, name: '苹果1', type: '电脑', price: '99999' },
  Good { id: 23, name: '小米2', type: '平板', price: '4999' },
  Good { id: 24, name: '华为2', type: '平板', price: '399' }
] 9

findAndCountBy(多少条记录,可筛选)

  • 可以指定条件
  • 会返回筛选之后的数据和条数
//数据价格是2000的
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"AppDataSource.initialize().then(async () => {
​
    const [goods, count] = await AppDataSource.manager.findAndCountBy(Good, {
        price: "2000"
    });
    console.log(goods, count);
}).catch(error => console.log(error))
​
​
//打印的数据
[
  Good { id: 18, name: '红米', type: '手机', price: '2000' },
  Good { id: 21, name: '小米1', type: '电脑', price: '2000' }
] 2

findOne(一条数据)

//查询 商品类型是手机的  只显示 id  和name   降序排序
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"AppDataSource.initialize().then(async () => {
​
    const goods = await AppDataSource.manager.findOne(Good, {
        select: ["id", "name"],  // 查询字段
        where: { type: "手机" }, // 查询条件
        order: { id: "DESC" }, // 排序  降序
    });
    console.log(goods);
}).catch(error => console.log(error))
​
​
//打印的数据
Good { id: 19, name: 'OPPO' }

这里排序没什么用,因为只有一条哈哈

findOne 只是比 find 多加了个 LIMIT 1,其余的都一样所以find可以用,看一下效果

// 查询 id是16.15的手机的  只显示 id  和name   降序排序
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"
import { In } from "typeorm"AppDataSource.initialize().then(async () => {
​
    const goods = await AppDataSource.manager.find(Good, {
        select: ["id", "name"],  // 查询字段
        where: { id: In([16, 15]) }, // 查询条件
        order: { id: "DESC" }, // 排序
    });
    console.log(goods);
}).catch(error => console.log(error))
​
//打印的数据  数据库没有id等于15的数据
[ Good { id: 16, name: '小米' } ]

findOneBy(后面直接对象写想查询的条件)

import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"AppDataSource.initialize().then(async () => {
​
    const goods = await AppDataSource.manager.findOneBy(Good, {
        id: 16
    });
    console.log(goods);
}).catch(error => console.log(error))
​
//查询出id是16的数据
Good { id: 16, name: '小米', type: '手机', price: '1000' }

query builder

  • 遇到极其复杂的 我们使用这种方式,这里简举例
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"AppDataSource.initialize().then(async () => {
​
    const queryBuilder = await AppDataSource.manager.createQueryBuilder();
​
    const goods = await queryBuilder.select("good").from(Good, "good")
        .where("good.id = :id", { id: 16 })
        .getOne()
    console.log(goods);
}).catch(error => console.log(error))
​
​
//打印出的数据
Good { id: 16, name: '小米', type: '手机', price: '1000' }

transaction

  • 多条有关联的数据的增删改都离不开事务

    • 开启事务
// transaction 方法包裹下就好
 
 await AppDataSource.manager.transaction(async manager => {
    await manager.save(User, {
        id: 4,
        firstName: 'eee',
        lastName: 'eee',
        age: 20
    });
});
​

简化增删改查

  • 可以先调用 getRepository 传入 Entity,拿到专门处理这个 Entity 的增删改查的类,再调用这些方法
//以前是这样的
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"AppDataSource.initialize().then(async () => {
​
    await AppDataSource.manager.save(Good, [
        { name: 'ccc', type: 'ccc', price: "21" },
        { name: 'ddd', type: 'ddd', price: "22" },
        { name: 'eee', type: 'eee', price: "23" }
    ])
}).catch(error => console.log(error))
​
​
//现在import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"AppDataSource.initialize().then(async () => {
​
    await AppDataSource.manager.getRepository(Good).方法
}).catch(error => console.log(error))
​