起步
从这里开始,我们将对nest.js的对数据操作的基本应用做一个简单的讲解,主要是明白如何通过访问接口来进行增删改查的基本操作。
介绍
这里我们使用的数据库是Mysql数据库,可以进入Mysql官方下载地址按照自己的系统选择下载,目前我这里使用的版本图如下:
工欲善其事必先利其器,接着就需要安装一个数据库的可视化应用,我这里下载的是Navicat软件。
一切准备就绪后,就可以进行下一个步骤了。
TypeORM
TypeORM 是一个ORM框架,它可以运行在 NodeJS、Browser、Cordova、PhoneGap、Ionic、React Native、Expo 和 Electron 平台上,可以与 TypeScript 和 JavaScript (ES5,ES6,ES7,ES8)一起使用。 它的目标是始终支持最新的 JavaScript 特性并提供额外的特性以帮助你开发任何使用数据库的(不管是只有几张表的小型应用还是拥有多数据库的大型企业应用)应用程序。 – 摘自官网
在nest.js
中,内置并推荐我们使用TypeORM
来操作数据库,至于什么是ORM?简单来说就是把JavaScript
的对象映射为数据的一张表,通过操作该对象来使得影响数据库中表的结果。
连接数据库
安装完数据库后,首先我使用Navicat来连接Mysql数据库,看看是否能成功连接,执行步骤如下:
- 进入Navicat软件,点击连接,会弹出一个提示框让我们输入凭证;
- 输入对应的地址和用户名、密码,点击测试连接,看到下图即表达连接可行:
- 点击OK进行连接,就完成了。
在Navicat中测试连接成功后,我们就需要在nest.js
来进行数据库的间接了,连接的代码如下所示:
1 | // app.module.ts |
- type: 表示要连接的数据库类型,因为TypeORM支持多种数据库;
- host:连接的目标主机号,我们是本地的服务,所以是localhost;
- port:数据库的端口号,mysql默认为3306;
- username:登入数据库的用户名;
- password:登入数据库的密码;
- database:要使用的mysql数据库;
- autoLoadEntities:自动加载实体类去映射到数据库;
- synchronize:是否同步本地的修改到数据库。
警告:synchronize严禁在生产环境下使用,因为有可能会导致数据的丢失。
完成以上配置后,执行npm run start:dev
启动服务,没有报错的话,就表示数据库已经连接成功了。
操作数据库
还记得之前建立的商品实体对象嘛?当时,这个对象就仅仅当做类型来使用了一下,并没有其他用处。但是,在我们是使用TypeORM
后,我们就可以让实体类和数据库进行一个映射关系。
关联实体
现在,先来改造一下实体类,代码如下:
1 | import { |
在上面的实体类代码中,我们通过@Entity()
装饰器修饰了该实体,并且通过@Column()
装饰器装饰每个字段,其中比较特殊的是@PrimaryGeneratedColumn()
,这个装饰器表示修饰的该字段是一个自增主键。
当实体类改造完毕以后,我们就需要在goods.service
中注入存储库对象,这个对象就是用来操作数据库的。在注入存储库之前,我们得先做一个操作,就是需要在goods.module
中进行注册,代码如下:
1 | import { TypeOrmModule } from '@nestjs/typeorm'; |
如果操作一切正确,打开Navicat
我们就能发现多了一张goods
表了!
注入存储库
通过TypeOrmModule.forFeature([Goods])
该方法后,就实现了存储库的注册,现在我们在goods.service
进行注入,代码如下:
1 | import { UpdateGoodsDto } from './dto/update-goods.dto'; |
可以看到,在构造函数中我们定义了一个通过@InjectRepository(Goods)
装饰器修饰的goodsRepository
对象,通过该对象我们就可以操作数据库了。
在下面的findGoodsById
方法中,就通过了findOneBy({ id })
方法对数据库进行了一个条件的查询,现在我通过Navigate
工具手动添加一条数据:
数据添加完成后,尝试进行对商品查询的接口调用,结果如下图:
一切都很好,说明我们的查询功能十分顺利。
存储库管理
可以看到,上面我们通过定义的goodsRepositroy
对象就可以查询数据库了,那么这个goodsRepositroy
是什么呢?
其实goodsRepositroy
就是存储库管理Repository
实例的对象,通过该对象我们可以访问指定实体对应的数据库关系,并且该对象提供了一系列方法供我们来使用,比如下面常用的4个Api
方法:
- find:查询出满足条件的所有数据;
- findOneBy:通过指定条件查询一条数据出来
- save:即可用作保存,也可用作修改操作;
- delete:根据条件删除数据;
表之间关联
之前我们定义Goods
实体的时候使用keywords
属性来存放关键字,但实际上关键字作为一个实体更合理。现在我们以Keywords
来新建另一个实体,并且让Goods
和Keywords
来进行一个关联。
新建Keywords
实体类文件,代码如下:
1 | import { Goods } from './goods.entity'; |
当程序编译完成,数据库中多了一张keywords
表,接下来我们就需要把Keywords
和Goods
实体关联起来。
不难知道,商品和关键字的关系是多对多的关系,因为一个关键字可以对应多个商品,一个商品也可以拥有多个关键字,所以这里我们需要TypeORM
提供的@ManyToMany()
装饰器来修饰,代码如下:
1 | // goods.entity.ts |
现在来说明一下上面代码的意思:
- @JoinTable():表示关系的拥有者,只需要在其中一个定义即可;
- @ManyToMany():表示两个实体间的关系为多对多
- arg1:表示需要建立关系的实体类对象;
- arg2:建立关系的实体类对象引用的当前实体类属性;
- options:cascade表示是否级联,级联之后添加和修改时,主表都会影响从表的记录。
注意:多对多关系会生成三张表,多出来的那一张是中间表,记录两个表数据的对应关系。
完成以上步骤后,我们现在尝试进行数据的保存,代码如下:
1 | // goods.service.ts |
上面我们通过顶一个preloadKeywordsByName
方法来根据关键字名称来加载实体:
- 表中存在关键字,存在直接返回该实体;
- 表中没有关键字,则创建一个实体对象;
然后通过传递进来的keywords
参数遍历生成对应的实体对象,进行save()
操作后nest.js
就会将我们的数据分别保存到三个表中。
通过接口调用,结果如下图:
可以看到,我们虽然只操作了Goods
实体的保存,但是也自动的在keywords
表中进行了数据的添加,这是因为cascade
级联起的作用。
好的,我们再进行查询,结果如图:
发现,没有keywords
关键字,这是因为当我们查询时,如果需要查询出关联的表的话,需要加上relations
参数来指定关联,如下所示:
1 | async findGoodsById(id: number) { |
通过添加relations
关联后,我们再进行查询,可以发现keywords
对应的信息也出现了,如下图:
分页查询
查询数据的时候,一般都会进行分页的一个筛选查询,否则当数据量大的时候,全部查询出来的效率是比较低的,也是不好处理的。
进行分页查询需要用到两个参数,一个是skip
表示跳过的记录数,另一个是take
表示需要查询的记录数量,代码如下:
1 | async findAll(paginationQueryDto: PaginationQueryDto) { |
我们通过上面的方式,来实现根据页数来查询对应数量的记录,尝试调接口看一看返回,如图:
结果发现,数据是能够分页信息查询出来的!
事务
当我们在做某个功能的时候,可能需要操作多次数据库,有可能在其中的某一次操作中会发生异常导致失败。但此时,成功的操作也无法逆转,导致数据出现问题。
此时,我们就需要通过事务来解决这个问题。简单说,事务就是保证多次操作数据库时的一致性,要么都成功,要么都失败。
使用事务时,我们需要通过注入的方式来引用,代码如下:
1 | () |
其中,private readonly dataSource: DataSource
定义了一个数据源对象,这个对象不用通过装饰器来装饰也能被nest.js
给我们进行管理。
接着,我们再来看看执行事务的代码,如下:
1 | async recommendGoods(id: number) { |
上面,我们通过dataSource
对象创建了一个QueryRunner
对象,通过该对象我们就可以建立连接,并手动控制事务的状态。
- startTransaction(): 开启事务;
- commitTransaction():提交所有的更改;
- queryRunner.rollbackTransaction():事务回滚,撤销所有更改;
- queryRunner.release():释放当前的连接;
数据迁移
数据迁移的作用在于,当我们可能需要修改表结构时:如果直接通过修改实体类的话,会导致表里面的数据造成丢失;所以需要通过数据迁移文件来操作数据库,进而达到修改表结构的目的。
现在,我们通过TypeORM CLI
来创建一个数据迁移的文件,输入以下命令:
1 | npx typeorm migration:create src/migrations/KeywordsRefactor |
执行该命令后,在我们的src/migrations
目录下就会生成一个{timestamp}-KeywordsRefactor.ts
的文件(timestamp表示自动生成的时间戳),进入该文件发下以下内容:
1 | import { MigrationInterface, QueryRunner } from 'typeorm'; |
其中,up
方法表示迁移所需要执行的sql
代码;而down
表示迁移失败之后,需要回复up
操作的方法。
例如,我们这里将keywords
表的name
字段改为title
字段,代码如下:
1 | import { MigrationInterface, QueryRunner } from 'typeorm'; |
在执行迁移之前,我们还需要创建一个db.config.ts
配置文件,内容如下:
1 | import { DataSource } from 'typeorm'; |
配置就和建立数据库连接时的配置基本一样,只是多了两个字段:
- entities:表示实体类的存储位置;
- migrations:表示迁移文件的存储位置;
因为这里我们迁移过程是根据打包后的dist
目录来识别的,所以在迁移之前先进行build
构建,执行命令:
1 | npm run build |
当生成好dist
目录后,我们就可以进行迁移命令了:
1 | npx typeorm-ts-node-esm migration:run -d db.config.ts |
看到终端提示如下信息的话,就表明迁移已经成功了:
并且数据库里面keywords
表的字段也已经被修改了:
此时,如果我们执行下面命令:
1 | npx typeorm-ts-node-esm migration:revert -d db.config.ts |
再次查看keywords
表结构,发现字段名称又被改回去了:
注意:执行了run命令迁移之后的文件会被记录到数据库中,不会再次执行;当revert后记录会被删除。
完结
基于nest.js
配合TypeORM
的数据库操作就简单介绍这里,实际上还有很多的API
提供使用,但内容太过繁多,不便细写。
评论区
欢迎你留下宝贵的意见,昵称输入QQ号会显示QQ头像哦~