浅谈MVC–Node中如何使用ORM?

  • 2019 年 10 月 11 日
  • 筆記

在正常的开发中,大部分都会使用MVC为主要的系统架构模式。而Model一般包含了复杂的业务逻辑以及数据逻辑,因为Model中逻辑的复杂度,所以我们有必要降低系统的耦合度。通常情况下,我们如果直接使用JDBC操作数据库,业务逻辑和数据存取逻辑是混在一起的。我们一般一个功能的逻辑可能如下所示:

  • 接收客户端的参数,建立数据库的连接。
  • 根据功能组装sql语句,然后创建Statement对象。
  • 使用Connection对象执行sql语句,得结果集ResultSet
  • 循环读取结果集的数据,然后根据数据进行业务逻辑处理。
  • 如果还有进一步的需求,再组装新的sql语句进行执行。
  • 执行结束关闭数据库连接。

可以看到上面业务逻辑和数据存取逻辑是紧密耦合在一起的,如果需要修改需求,那工作量则是成倍的增长。所以有必要将业务逻辑以及数据存取逻辑分离开来,所以产生了ORM这么一个对象与数据之间的映射技术。简单来说ORM就是通过实例对象的语法,完成对关系型数据库操作的技术,是对象-关系映射的缩写。而本篇文章主要介绍一个NodeJS环境下的ORM框架—Sequelize

首先,我们基于脚手架快速搭建一个express项目,执行命令:

express testORM

进入项目并且安装依赖:

npm install

首先,我们先改变一下项目目录结构:

  • 项目已有目录routes下存放路由文件。
  • 根目录下创建config.js,存放常量参数。
  • 根目录下创建contonller文件夹,在contonller下创建db.js,里面封装Sequelize连接数据库的操作。
  • 根目录下创建db文件夹,在contonller下创建pay_goods.js,里面定义数据类型,封装数据库存取的操作。
  • 根目录下创建service文件夹,在service下创建pay_goods.js,里面对数据进行业务逻辑处理。
  • 根目录下创建utils文件夹,用来存放全局方法。

所以我们生成的项目最终项目结构如下:

首先要使用SequeLize,我们需要安装sequelizemysql2包。命令如下:

npm install --save-dev sequelize mysql2

首先在config.js中配置数据库连接常量:

然后进入contonller/db.js初始化数据库连接:

然后进入db/pay_goods.js中,在这里负责对数据表进行数据类型定义以及数据读取操作。我们首先使用sequelize.define()针对pay_goods表定义数据类型:

define()方法共存在三个参数:

  • 参数1:表示映射的数据库表名
  • 参数2:对表中每一个对象进行数据类型定义。
  • 参数3:对选填参数配置

这里对于Sequelize中的数据类型直接贴下文档中提供的:

数据类型定义需要注意一点,如果我们有插入操作,Sequelize默认会增加createdAt字段和updateAt字段,所以说如果我们不需要这两个字段我们可以在参数3选填参数添加timestampsfalse关闭添加这两个参数的操作。

对映射的数据表定义好数据类型,接下来我们可以写几个简单的数据库存取操作。Sequelize提供的API是非常丰富的,一篇文章不可能一一讲解,所以我就选几个比较通用的API。首先我们通过定义的数据类型调用create()可以保存数据,这里我们在外层封装一层function可以接受来自路由层的参数:

接下来进入utils/common.js实现一个获取客户端传参的全局方法:

进入routes/users.js实现路由,调用全局方法paramAll()获取客户端传参,然后参数处理等业务逻辑我们放在service下的pay_goods.js,所以调用service/pay_goods.js封装的参数处理方法calcBalance()对参数进行处理:

参数处理完返回给路由层,然后调用db中我们刚才封装的saveOrder()保存数据:

上面就完成的实现了一个插入数据的API,我们不需要手动书写sql语句。而且将业务逻辑和数据存取逻辑完全独立。接下来我们可以再看几个查询语句:

可以看到我们查询一共写了三个典型的示例方法,我们来分别看看是查询什么样的数据:

  • findAll():查询多条数据,传入一个json对象,json对象中可以对查询条件进行限制,比如我示例代码中使用attributes传入要查询的数据列数组,使用where传入where条件语句的参数限制,使用limitoffset参数可以进行分页操作,使用order可以根据某个数据列操作等。
  • findByPk():通过id查询对应数据,id一般为主键,所以只会返回一条数据,而且参数只能传入id
  • findOne():只能查询一条语句,一样可以指定findAll()中的各种条件,但是只会返回符合条件的第一条数据。

到这里我们对于Sequelize的基础操作就差不多了解了,接下来来看看Sequelize封装sql如何链式调用多个数据库操作,因为Sequelize是基于PromiseORM框架,所以我们很简单的使用链式调用数据库读取操作实现多个数据库操作:

到这里我们就可以实现MVC架构,将数据库数据读取操作封装到db层,将路由操作封装到routes层,将业务逻辑操作封装到service下。有利于项目的不断迭代开发。