面向前端工程师的 Node.js 入门手册(四)

数据库,网站系统最重要的部分之一,它好比一个人的大脑,可以记下开发者们想让它记下任何的事情,而且它比人脑更可靠更精准。

实质上,任何数据库均是文件系统,但是它与我们在桌面上右键新增的文件相比而言,数据库则是有规则的文件系统,不像我们普通新增一个文件便可以随意写东西进去,数据库文件会有专门的存贮规则和特定操作数据内容的方式。

最常见的SQL语句其实就是一种操作规范,它约束了增删改查必须要通过规定的方式,像select,update, delete等特定语句,最终将生成的是规范化数据内容。

接下来看看Nodejs能不能操作这个网站的“记忆系统”呢?如果可以操作又是如何操作的呢?一起进入Nodejs与数据库的内容学习吧。

文件数据库

数据库的本质是存储数据,我们平时用的文件本身也是存储数据,那么我们只要制定一个规范,那普通文件也可以是一个数据库,而且普通文件不依赖环境,你不必安装引擎或者工具之类的才能操作,它是操作系统自带的能力,所以某些场景更符合使用。

举个栗子,比如你的应用是客户端应用,像一些客户端配置或者状态数据并不想通过上传到云端的服务器上,而就是想存在客户端本地,起到类似于浏览器上的localStorge的作用,这时候你便可以新增一个文件作为数据库来使用。

在Nodejs中,lowdb模块[1]便是被用于文件数据库的封装库,它的规范就是我们熟知的json规范,使用它无需安装其他软件,仅需要我们有nodejs环境即可。

安装

npm i --save lowdb

示例:

// app.js
const low = require('lowdb');
const FileSync = require('lowdb/adapters/FileSync');

// 同步文件类型
const adapter = new FileSync('db.json');
const db = low(adapter);

// 初始化数据库字段
db.defaults({ userInfo: {}, time: '' }).write();

db.set('userInfo.name', '全栈者').write();
db.set('userInfo.title', '欢迎关注').write();
db.set('time', new Date()).write();

const userInfo = db.get('userInfo').value();
const time = db.get('time').value();

console.log(`userInfo: %o`, userInfo);
console.log(`time: %s`, time);
// userInfo: { name: '全栈者', title: '欢迎关注' }
// time: Tue Aug 27 2019 16:06:13 GMT+0800 (CST)

再看看db.json文件里的内容

{
  "userInfo": {
    "name": "全栈者",
    "title": "欢迎关注"
  },
  "time": "2019-08-27T08:06:13.991Z"
}

非关系型数据库

非关系型数据库也是一种非常常用的数据库,一般的我们所用到的MongoDB,CouchDB都属于此类,非关系型的数据库和上面的文件数据库其实很类似,它也是基于键值对作为存储规范。

但是相比于上面来说,它的自身做了很多限制与规范。它被广泛使用在非关系数据的存储上,性能相比较与关系型数据库也是非常不错,一般大型的应用都会将非关系数据库与关系型数据库的共同协作使用。

这里就以Mongodb来看看Nodejs是如何操作非关系型数据库的。

首先安装Mongodb,这里还是推荐使用docker去安装mogodb。

docker search mongo
docker pull mongo
# 拉下来之后启动的时候要把本机的数据文件位置与docker容器进行关联
# 在docker中使用 -v 进行挂载
# docker启动镜像, -p 暴露27017端口, 
# 下面的文件路径要替换成你的机器上的一个要存放db文件的文件路径,比如我在 ~/Desktop/Practice-book/nodejs/db/mongodb/db 
# 下存放我的db文件,那我的文件路径就是 ~/Desktop/Practice-book/nodejs/db/mongodb/db
docker run -p 27017:27017 -v ~/Desktop/Practice-book/nodejs/db/mongodb/db:/data/db -d mongo
# 启动完成查看一下
docker ps

~/Desktop/Practice-book/nodejs/db/mongodb/db 文件夹下会多出一些如下文件。

接下来进行连接与操作mongodb数据库,这里选用使用量较高的mongoose模块。

安装mongoose

cnpm i --save mongoose

示例:

const mongoose = require('mongoose');

mongoose.connect('mongodb://127.0.0.1:27017/db', { useNewUrlParser: true });

mongoose.connection.on('error',() => {
  console.log('连接错误:')
});

// 定义存储数据的sechema
const Sechema = new mongoose.Schema({
  name: String,
  title: String,
  time: Date,
});

// 定义数据模型,模型即可直接操作数据,如创建查询更新删除等。
const Model = mongoose.model('person',Sechema);

Model.create({
  name: '全栈者',
  title: '欢迎关注',
  time: new Date(),
})

Model.find({}, function(err, data) {
  if(err) {
    console.error(err);
  } else {
    console.log(data);
  }
});

Model.findById('5d64f210e38a73dce44956bf', function(err, data) {
  if(err) {
    console.error(err);
  } else {
    console.log('id: 5d64f210e38a73dce44956bf');
    console.log(data);
  }
});

上面这段代码,先连接了docker提供的mongodb服务,然后定义了我们要存进Mongodb的数据Sechema,Sechema的作用就是限定存入mongodb的字段数据类型,如Number,String等基本类型。

接着定义了一个模型Model,Model即可理解为暴露出的一张表的操作对象,如新增查找更新删除等都在Model上,例子中的Model就是操作person表的操作对象,它里面有find,create等一些方法。重复执行了几次node app后,看看上面代码的执行结果。

关系型数据库

关系数据库是目前使用体量最大,最广泛的数据库了,它的优点非常明显,首先从它二维表的结构设计是非常贴近逻辑世界概念,关系模型相对网状、层次等,对人来说很容易理解,同时它丰富的完整性也大大减低了数据冗余和数据不一致的概率,保证数据的准确一致性。

还有最大的亮点就是支持SQL语句了,有了SQL语句很多复杂的查询都可以被实现,如多个表之间的操作便可以通过一个SQL语句实现,非常便捷。当然同时也因为多了SQL层解析,它相比于非关系型数据库读写性能相对较低。

在这里的所演示的关系型数据库采用最常用的mysql,来看看Nodejs是如何操作关系型数据库mysql的。

1. 首先安装mysql,这里还是使用docker去安装mysql,和上面mongodb的安装一样的步骤。

docker search mysql
docker pull mysql
# 拉下来之后启动的时候要把本机的数据文件位置与docker容器进行关联
# docker启动镜像, -p 暴露3306端口
docker run -p 3306:3306  -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.6
# 启动完成查看一下
docker ps

2. 安装可视化操作工具,笔者推荐datagrip工具操作数据库,但是要收费(囧),我这边使用免费的sequelPro。

3. 进行建库建表操作,使用建表语句[2] 建表完成。

4. 接下来使用Nodejs来操作mysql,这里使用mysql模块来演示。

安装mysql模块

cnpm i --save mysql

示例:

const mysql = require('mysql');

const connection = mysql.createConnection({
  host: '127.0.0.1',
  port: 3306,
  user: 'root',
  password : '123456',
  database : 'Test_DB'
});
 
connection.connect(function(err) {
  if (err) {
    console.error('error connecting: ' + err.stack);
    return;
  }
  console.log('connected as id ' + connection.threadId);
});

const insert = `INSERT INTO Tab_User_Info (name, title, description) VALUES ('全栈者', '欢迎关注', '微信公众号:全栈者')`

// 插入一条数据到Tab_User_Info表
connection.query(insert, function (error, results) {
  if (error) console.log(error);
  // affectedRows 影响行数,为1时则证明插入成功了
  if (results.affectedRows === 1) {
    console.log('插入成功');
    selectTable();
  }
});

// 查询Tab_User_Info表所有数据
function selectTable() {
  const select = `Select * from Tab_User_Info`;
  connection.query(select, function (error, results) {
    if (error) console.log(error);
    console.log(results)
  });
}

上面Nodejs操作Myql的例子完成了,首先我们使用mysql提供的createConnection接口连接docker启动的mysql服务,然后编写插入SQL语句,使用连接数据库后query接口进行执行编写好的SQL语句,执行完成之后进行一次查询。

5. 结果如下

实战

1. 需求

给前端提供一个接口,该接口内容可以由mogodb提供,也可以由mysql提供,但是由那个数据库提供并非随机决定的,而是需要内部人员进行开关控制。

2. 实现思路

a. 首先可以根据需求要提供两个接口,一个是内部人员使用的开关接口,另一个是提供给前端使用的数据接口。b. 开关接口只需要存储当前数据接口处于那种模式,是一种状态值,在某一时刻只处于一种状态 ,所以这里适合使用FileDB就记录状态。c. 数据接口的提供者由FileDB内的状态值来决定,所以在用户数据接口请求时先获取FileDB内的状态判断。3. 代码示例:

// http.js
const http = require('http');
const url = require('url');
const qs = require('querystring');

const fileDB = require('./fileDB');
const mongodb = require('./mongodb');
const mysql = require('./mysql');

const genResponse = message => JSON.stringify({
  success: true,
  data: message
});

http.createServer((req, res) => {
  res.setHeader('Content-Type', 'application/json;charset=utf-8');
  const reqUrl = url.parse(req.url);
  if (reqUrl.pathname === '/api/set/db') {
    // 开关接口
    const { db } = qs.parse(reqUrl.query);
    if (['mongo', 'mysql'].includes(db)) {
      fileDB.set('curDb', 'mongo').write();
    } else {
      return res.end(genResponse('参数有误'));
    }
    fileDB.set('curDb', db).write();
    fileDB.set('updateTime', new Date()).write();
    const result = genResponse(`修改数据库模式为:${db}`);
    res.end(result);
  } else if (reqUrl.pathname === '/api/get/data') {
    // 数据接口
    const db = fileDB.get('curDb').write(); //获取当前状态
    if (db === 'mongo') {
      // 获取mogondb数据
      mongodb.find({}, function(err, data) {
        if(err) {
          console.error(err);
          return res.end(genResponse(err));
        } else {
          const result = genResponse(data);
          res.end(result);
        }
      });
    } else {
      // 获取mysql数据
      const select = `Select * from Tab_User_Info`
      mysql.query(select, function (error, results) {
        if (error) {
          console.log(error)
          res.end(genResponse(error));
        } else {
          res.end(genResponse(results));
        }
      });
    }
  } else {
    res.writeHeader(404);
    res.end('NotFund');
  }
}).listen(8000, ()=> {
  console.log('listen on 8000!');
})

上面的示例中提供了一个开关接口 /api/set/db , 该接口由内部人员触发,来设置数据接口的提供者,另一个是数据接口/api/get/data ,该接口用来给前端提供数据,该接口被触发时,先要获取开关接口所设置的状态值,然后执行查操作,之后返回数据。 4. 结果展示 a. 开关接口设置数据库为mysql

b. 开关接口设置数据库为mongo

References

[1] lowdb模块: https://www.npmjs.com/package/lowdb [2] 建表语句: https://github.com/FantasyGao/Practice-book/blob/master/nodejs/db/mysql/test.sql

本文所用的的代码均可在下面找到,有兴趣的clone下来动手练习。

文章用到的代码均可在此获取: https://github.com/FantasyGao/Practice-book/tree/master/nodejs/db

本文分享自微信公众号 - Nodejs技术栈(NodejsDeveloper)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-10-24

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券