node+express+grunt+mysql操作数据库案例分析

简单唠两句、

    在以前的node操作数据库,都是选择mongodb这类型的非关系型数据库,进行数据存储操作。而这次业务涉及到mysql,所以来简单了解下node环境下,是如何操作mesql数据库增删改查的?

一、安装

1. 开发依赖

yarn init
yarn add mysql
yarn add express
yarn global add add json-server # 本地服务工具
yarn global add add nodemon  / 或者 yarn add --dev grunt grunt-contrib-watch grunt-execute     # 自动化工具

2. 数据库工具:Navicat for MySQL

  **版下载地址:https://pan.baidu.com/s/1u8GGJ5AFFvAJKbXij4NyMA 提取码: vhzc

二、开发测试思路

采用json-server启动本地服务,默认启动public目录下index.html文件,在index.html文件中fetch请求express启动的服务,根据请求的对应路由操作mysql数据库

D:\me\npm\node-mysql>json-server db.json --port 3006

  \{^_^}/ hi!

  Loading db.json
  Done

  Resources
  http://localhost:3006/posts
  http://localhost:3006/comments
  http://localhost:3006/profile

  Home
  http://localhost:3006

  Type s + enter at any time to create a snapshot of the database


三、开发过程


1. 启动mysql服务,新建数据库,插入一些测试数据


(1)执行sql脚本:

CREATE TABLE employees (
  id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(50),
  location varchar(50),
  PRIMARY KEY (id)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ;

INSERT INTO employees (id, name, location) VALUES
(1, 'Jasmine', 'Australia'),
(2, 'Jay', 'India'),
(3, 'Jim', 'Germany'),
(4, 'Lesley', 'Scotland');

(2)或者手动插入

node+express+grunt+mysql操作数据库案例分析


2. 新建/server.js文件、public/index.html文件


(1)server.js中开启服务,配置允许跨域请求,连接mysql数据库

// server.js
const mysql = require('mysql');
const app = require('express')();

//allow cross origin
app.all('*', function (req, res, next) {
  res.header('Access-Control-Allow-Origin', '*');
  //Access-Control-Allow-Headers ,可根据浏览器的F12查看,把对应的粘贴在这里就行
  res.header('Access-Control-Allow-Headers', 'Content-Type');
  res.header('Access-Control-Allow-Methods', '*');
  res.header('Content-Type', 'application/json;charset=utf-8');
  next();
});

// First you need to create a connection to the db
const con = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: 'root',
  database: 'node'
});

con.connect((err) => {
  if(err){
    console.log('Error connecting to Db');
    return;
  }
  console.log('Connection established');
});

con.end((err) => { //注:在end之后就不能操作数据库了! });

(2) 启动node服务

C:\web\npm\node-mysql>node server.js
Connection established


(3) 根据需求或者个人喜好安装nodemon/grunt自动编译工具,如下是grunt的安装

2-3-1. 安装

C:\web\npm\node-mysql>cnpm install -g grunt grunt-contrib-watch grunt-execute

2-3-2. 查看版本

C:\web\npm\node-mysql>grunt --version
grunt-cli v1.2.0
grunt v1.0.3

2-3-3. 新建一个Gruntfile.js文件

module.exports = (grunt) => {
  grunt.initConfig({
    execute: {
      target: {
        src: ['./server.js']
      }
    },
    watch: {
      scripts: {
        files: ['./server.js'],
        tasks: ['execute'],
      },
    }
  });

  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-execute');
};

2-3-4. 执行监听

C:\web\npm\node-mysql>grunt watch
Running "watch" task
Waiting...
>> File "server.js" changed.
Running "execute:target" (execute) task
-> executing C:\web\npm\node-mysql\server.js
Connected !
-> completed C:\web\npm\node-mysql\server.js (254ms)

>> 1 file and 0 calls executed (270ms)

Done.
Completed in 0.835s at Tue Oct 23 2018 21:44:08 GMT+0800 (中国标准时间) - Waiting...

(4)index.html文件中发出fetch请求服务,如下

fetch('http://localhost:3000/delete').then(res=>res.json()).then((res)=>{
    document.querySelector('#box').innerHTML += JSON.stringify(res);
});


2.查询所有数据

// 查询所有信息
app.get('/getAll', function(req, res){
    connection.query('SELECT * FROM employees', (err,rows) => {
        res.send(rows);
    });
});

3. 插入数据信息

app.get('/set', function(req, res){
    console.log('123');
    const employ = {name: '小虎的家乡', location: '九寨沟'};
    connection.query('INSERT INTO employees SET ?',employ, (err,doc) => {
        res.send(doc);
        //{"fieldCount":0,"affectedRows":1,"insertId":5,"serverStatus":2,"warningCount":0,"message":"","protocol41":true,"changedRows":0}
    });
});

4. 更新数据

app.get('/update', function(req, res){
    const employ = ['大明王朝的芹菜',1];
    connection.query('update employees SET location = ? where ID = ? ',employ, (err,doc) => {
        res.send(doc);
        //{"fieldCount":0,"affectedRows":1,"insertId":0,"serverStatus":2,"warningCount":0,"message":"(Rows matched: 1 Changed: 1 Warnings: 0","protocol41":true,"changedRows":1}
    });
});

5. 删除信息

app.get('/delete', function(req, res){
    connection.query('DELETE FROM employees WHERE id = ?',4, (err,doc) => {
        res.send(doc);
        //{"fieldCount":0,"affectedRows":1,"insertId":0,"serverStatus":2,"warningCount":0,"message":"","protocol41":true,"changedRows":0}
    });
});

四、使用存储过程查询数据库

1. 首先需要在navicat中执行如下查询结构:

DELIMITER $$

CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_getall`()
BEGIN
  SELECT id, name, location FROM employees;
END

2. 然后在server.js中执行查询服务

connection.query('CALL sp_getall()',function(err,rows){
    if(err) throw err;

    console.log('Data received form Db: \n');
    console.log(rows);
});

3. 打印如下:

Data received form Db:

[ [ RowDataPacket { id: 1, name: 'Jasmine', location: 'Australia' },
    RowDataPacket { id: 2, name: 'Jay', location: 'India' },
    RowDataPacket { id: 3, name: 'Jim', location: 'Germany' },
    RowDataPacket { id: 4, name: 'Lesley', location: 'Scotland' },
    RowDataPacket { id: 5, name: null, location: null } ],
  OkPacket {
    fieldCount: 0,
    affectedRows: 0,
    insertId: 0,
    serverStatus: 34,
    warningCount: 0,
    message: '',
    protocol41: true,
    changedRows: 0 } ]


五、使用存储过程传递参数查询

1. 执行sql语句

DELIMITER $$

CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_get_employee_detail`(
  in employee_id int
)
BEGIN
  SELECT name, location FROM employees where id = employee_id;
END

2. server.js中执行查询

// 使用存储过程查询id为1的数据
connection.query('CALL sp_get_employee_detail(2)',function(err,rows){
    if(err) throw err;
    
    console.log('Data received form Db: \n');
    console.log(rows);
});

3. 打印如下:

Data received form Db:

[ [ RowDataPacket { name: 'Jasmine', location: 'Australia' } ],
  OkPacket {
    fieldCount: 0,
    affectedRows: 0,
    insertId: 0,
    serverStatus: 2,
    warningCount: 0,
    message: '',
    protocol41: true,
    changedRows: 0 } ]


五、使用存储过程插入数据操作

1. 执行sql语句

DELIMITER $$

CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_insert_employee`(
  out employee_id int,
  in employee_name varchar(25),
  in employee_location varchar(25)
)
BEGIN
  insert into employees(name, location)
  values(employee_name, employee_location);
  set employee_id = LAST_INSERT_ID();
END

2. `query查询` (前置条件:需要把connect的连接参数对象中增加字段 multipleStatements: true )

connection.query( "SET @employee_id = 0; CALL sp_insert_employee(@employee_id, 'tiger', 'forest'); SELECT @employee_id",function(err,rows){
    if(err) throw err;

    console.log('Data received form Db: \n');
    console.log(rows);
});

3. 打印如下:

Data received form Db:

[ OkPacket {
    fieldCount: 0,
    affectedRows: 0,
    insertId: 0,
    serverStatus: 10,
    warningCount: 0,
    message: '',
    protocol41: true,
    changedRows: 0 },
  OkPacket {
    fieldCount: 0,
    affectedRows: 1,
    insertId: 0,
    serverStatus: 10,
    warningCount: 0,
    message: '',
    protocol41: true,
    changedRows: 0 },
  [ RowDataPacket { '@employee_id': 11 } ] ]


六、安全转义参数

const userLandVariable = 4;
connection.query(
  `SELECT * FROM employees WHERE id = ${mysql.escape(userLandVariable)}`,
  function(err, rows){ 
    console.log(rows);
  }
);


// [ RowDataPacket { id: 4, name: 'Lesley', location: 'Scotland' } ]


七、结论

  关于mysql的操作,还有很多相关的api,想参考更加详细的内容,请阅读:https://www.npmjs.com/package/mysql

 

无论什么时候,不管遇到什么情况,我绝不允许自己有一点点灰心丧气。—— 爱迪生