从零学习node.js之mysql数据库的操作(五)


Posted in Javascript onFebruary 24, 2017

准备工作

在使用node操作mysql数据库时,需要先下载mysql模块:

npm install mysql --save-dev

在引入mysql模块后,就可以进行数据库的连接和其他的操作了。

// test.js
var mysql = require('mysql');

一、连接数据库

首先保证本地已经安装数据库,并已正常启动,然后开始进行连接:

// test.js
var mysql = require('mysql');

// 创建连接
var conn = mysql.createConnection({
 host : '127.0.0.1',
 user : 'root',
 password : '123',
 database : 'test'
});

// 创建连接后不论是否成功都会调用
conn.connect(function(err){
 if(err) throw err;
 console.log('connect success!');
});

// 其他的数据库操作,位置预留

// 关闭连接时调用
conn.end(function(err){
 if(err) throw err;
 console.log('connect end');
})

执行node test.js后,就会输出:

$ node test.js
connect success!
connect end

连接成功,然后连接关闭。这就说明程序可以正常连接数据库了,然后就开始进行增删改查的操作。

二、CURD

比如我们有这样的一个user表,里面有4个字段,其中uid是自增字段:

uid username password email
1 meizi meizi 123@qq.com
2 test test 456@qq.com

我们就对这个表进行增删改查的操作。

mysql中有个query方法可以用来执行任意正确的sql语句,然后在回调函数里给出执行sql语句后的结果。query方法是异步执行的,若并列书写多个query方法的话,是不能按照书写顺序依次阻塞式执行的。

2.1 查询

使用最普遍最多的就是查询操作了。

conn.query('SELECT * FROM `user`', function(err, result, fields){
 if(err) throw err;
 console.log(result);
});
console.log( 'select ended!' );

输出的结果:

select ended! // 先输出

[
 RowDataPacket {   
 uid: 1,   
 username: 'meizi',  
 password: 'meizi
 email: '123@qq.com'
 }, 
 RowDataPacket {   
 uid: 2,   
 username: 'test',  
 password: 'test
 email: '456@qq.com'
 }
]

可以看到,结果集是一个数组,数组中的每条数据都是一个RowDataPacket对象,在使用时,可以像json对象一样获取数据,也可以使用JSON.stringify把result转换为json字符串,但是,result并不是JSON数据。而且,即使结果集中只有一条数据,也是以数组的形式返回的。

console.log(result[0].username); // meizi

输出即为字符串类型的数据。

2.2 添加

向数据库中添加数据使用的是INSERT,INSERT语句有两种形式都可以使用:

第1种,先列好要插入的数据对应的字段,然后跟上数据(如果要给所有的字段都插入数据,可以省略字段不写,但是数据的书写顺序要跟数据表里的字段一一对应):

INSERT INTO table_name ( field1, field2,...fieldN ) VALUES ( value1, value2,...valueN );

第2种,可以像update操作一样书写,将field与value对应的更紧密:

INSERT INTO table_name SET field1=value1, field2=value2, ... fieldN=valueN;

我更加喜欢第2种方式,这种方式更能看出操作了哪些字段,看出字段和数据的对应关系。在node中插入数据:

conn.query("INSERT INTO `user` SET `username`='qwerty', `password`='741', `email`='qwerty@qq.com'", function(err, result){
 if(err) throw err;
 console.log(result);
});

插入数据后返回的结果是:

OkPacket {    
 fieldCount: 0,  
 affectedRows: 1,
 insertId: 4, // 数据插入成功时,对应的主键id
 serverStatus: 2,  
 warningCount: 0,  
 message: '',   
 protocol41: true,  
 changedRows: 0
 }

affectedRows表示数据表中受影响的行数,数据插入成功则为1,失败则为0;在主键自增的情况下,insertId是数据插入成功后对应的主键id,如果主键不自增,则insertId为0。

2.3 更新

使用update语句更新数据:

// 更新uid的密码
conn.query('UPDATE `user` SET `password`="123456" WHERE `uid`=4', function(err, result){
 if(err) throw err;
 console.log(result);
});

输出的结果:

OkPacket {
 fieldCount: 0,
 affectedRows: 1,
 insertId: 0,
 serverStatus: 2,
 warningCount: 0,
 message: '(Rows matched: 1 Changed: 1 Warnings: 0',
 protocol41: true,
 changedRows: 1
 }

可以看到输出结果的类型与插入数据时输出结果的类型是一样的。我们分析一下,执行sql语句后,有3种结果:

  1. 成功修改数据: affectedRows:1, changedRows:1
  2. 要修改的数据与原数据相同: affectedRows:1, changedRows:0
  3. 未找到需要修改的数据: affectedRows:0, changedRows:0

因此可以根据这两个字段输出相应的结果。

2.4 删除

使用delete语句删除语句:

conn.query('DELETE FROM `user` WHERE `uid`=4', function(err, result, fields){
 if(err) throw err;
 console.log(result);
});

输出的结果:

OkPacket {
 fieldCount: 0,
 affectedRows: 1,
 insertId: 0,
 serverStatus: 2,
 warningCount: 0,
 message: '',
 protocol41: true,
 changedRows: 0
 }

删除成功,则affectedRows为1,删除的数据不存在,则为0。

三、连接池

数据库连接是一种有限的,能够显著影响到整个应用程序的伸缩性和健壮性的资源,在多用户的网页应用程序中体现得尤为突出。

数据库连接池正是针对这个问题提出来的,它会负责分配、管理和释放数据库连接,允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个连接,释放空闲时间超过最大允许空闲时间的数据库连接以避免因为连接未释放而引起的数据库连接遗漏。

3.1 创建连接池

使用mysql.createPool()可创建连接池:

// test.js
var mysql = require('mysql');

var pool = mysql.createPool({
 host : '127.0.0.1',
 user : 'root',
 password : '123',
 database : 'test'
})

pool.query('SELECT * FROM `user`', function(err, result){
 if(err) throw err;

 console.log(result);

 pool.end(function(err){
  if(err) throw err;
  console.log('connection ended');
 })
});

getConnection()可以共享一个连接,或管理多个连接。

// test.js
var mysql = require('mysql');

var pool = mysql.createPool({
 host : '127.0.0.1',
 user : 'root',
 password : '123',
 database : 'test'
})

pool.getConnection(function(err, connection){
 if(err) throw err;

 connection.query('SELECT * FROM `user`', function(err, result){
  if(err) throw err;

  console.log(result);
 })
});

连接使用完后通过调用connection.release()方法可以将连接返回到连接池中,这个连接可以被其它人重复使用:

pool.getConnection(function(err, connection){
 if(err) throw err;

 connection.query('SELECT * FROM `user`', function(err, result){
  if(err) throw err;

  console.log(result);
  connection.release();
  // 接下来connection已经无法使用,它已经被返回到连接池中 
 })
});

可以使用connection.destroy()彻底销毁连接。

3.2 连接池事件

createPool()方法会返回一个连接池实例对象,这个对象中有一些事件。

connection

连接池中产生新连接时会发送'connection'事件:

pool.on('connection', function (connection) {
 console.log('new connection');
});

3.3 QUERY与GETCONNECTION的区别

这两个方法都能进行操作,那么这两者有什么区别呢?

pool.getConnection中的connection在其回调函数里是一直的,可以保证这一系列的操作都是在同一个connection中执行的;pool.query则每次执行时可能会在不同的connection中执行,可能会得到意想不到的结果。

比如SQL_CALC_FOUND_ROWSFOUND_ROWS这需要两个sql语句完成,是获取检索行的数目。

pool.query('SELECT SQL_CALC_FOUND_ROWS * FROM `user`');
pool.query('SELECT FOUND_ROWS()');

这两个可能在不同的connection中执行,第2个sql语句返回的就不是上一个sql语句的结果了。

四、sql防注入

sql防注入的关键就是不能直接把数据拼接到sql语句中,必须得对数据进行转义,或者使用提供的方法拼接sql语句。这里主要有四种方法可以使用。

4.1 使用ESCAPE()对参数进行编码

参数编码方法有:mysql.escape()/connection.escape()/pool.escape() ,这三个方法可以在你需要的时候调用:

var sql = 'SELECT * FROM `user` WHERE `uid`='+connection.escape('"123";//--');
console.log(sql); // SELECT * FROM `user` WHERE `uid`='\"123\";//--'

connection.query(sql, function(err, result){
 if(err) throw err;

 console.log(result);
})

对双引号进行了安全转义。

escapeId()可以对不信任的表名,字段名进行转义。

var sql = 'SELECT * FROM '+connection.escapeId('user')+' WHERE `uid`=1';
connection.query(sql, function(err, result){
 console.log(result);
})
console.log(query.sql); // SELECT * FROM `user` WHERE `uid`=1

同时,escape()的编码规则如下:

  1. Numbers不进行转换
  2. Booleans转换为true/false
  3. Date对象转换为'YYYY-mm-dd HH:ii:ss'字符串
  4. Buffers转换为hex字符串,如X'0fa5'
  5. Strings进行安全转义
  6. Arrays转换为列表,如[‘a', ‘b']会转换为'a', ‘b'
  7. 多维数组转换为组列表,如[[‘a', ‘b'], [‘c', ‘d']]会转换为(‘a', ‘b'), (‘c', ‘d')
  8. Objects会转换为key=value键值对的形式。嵌套的对象转换为字符串
  9. undefined/null会转换为NULL
  10. MySQL不支持NaN/Infinity,并且会触发MySQL错误

4.2 占位符

可以使用?作为参数占位符。在使用查询参数占位符时,在其内部自动调用 connection.escape() 方法对传入参数进行编码。

var params = ['test', 'test'];
var query = connection.query('SELECT * FROM `user` WHERE `username`=? AND `password`=?', params, function(err, result){
 console.log(result);
});
console.log(query.sql); // SELECT * FROM `user` WHERE `username`='test' AND `password`='test'

同时,如果执行添加或更新操作时,还可以这样写:

var params = {username:'qwerty', password:'qwerty', email:'qwerty@qq.com'};
var query = connection.query('INSERT INTO `user` SET ?', params, function(err, result){
 if(err) throw err;
 console.log(result);
});
console.log(query.sql); // INSERT INTO `user` SET `username` = 'qwerty', `password` = 'qwerty', `email` = 'qwerty@qq.com'

数据库中的表明和字段名,可以使用??作为占位符,在拼接完成后会自动添加上``:

var params = ['user', 'username', 'test', 'password', 'test'];
var query = connection.query('SELECT * FROM ?? WHERE ??=? AND ??=?', params, function(err, result){
 console.log(result);
})
console.log(query.sql); // SELECT * FROM `user` WHERE `username`='test' AND `password`='test'

4.3 使用MYSQL.FORMAT()转义参数

不多说,样例如下:

var userId = 1;
var sql = "SELECT * FROM ?? WHERE ?? = ?";
var inserts = ['user', 'uid', userId];
sql = mysql.format(sql, inserts); // SELECT * FROM `user` WHERE `uid` = 1

五、多语句查询

出于安全考虑node-mysql默认禁止多语句查询(可以防止SQL注入),启用多语句查询可以将multipleStatements选项设置为true:

var connection = mysql.createConnection({multipleStatements: true});

启用后可以在一个query查询中执行多条语句:

connection.query('SELECT 1; SELECT 2', function(err, results) {
 if (err) throw err;

 // `results`是一个包含多个语句查询结果的数组
 console.log(results[0]);
 console.log(results[1]);
});

总结

本节只是总结了node对mysql数据库的各种操作,但如果实际应用起来的话,还远远不够。努力学习中…希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

Javascript 相关文章推荐
一些易混淆且不常用的属性,希望有用
Jan 29 Javascript
JavaScript让IE浏览器event对象符合W3C DOM标准
Nov 24 Javascript
多浏览器支持的右下角浮动窗口
Apr 01 Javascript
通过Javascript创建一个选择文件的对话框代码
Jun 16 Javascript
js获取和设置属性的方法
Feb 20 Javascript
js实现刷新iframe的方法汇总
Apr 27 Javascript
javascript实现随机读取数组的方法
Aug 03 Javascript
vueJS简单的点击显示与隐藏的效果【实现代码】
May 03 Javascript
node.js操作MongoDB的实例详解
Oct 11 Javascript
vue单页面实现当前页面刷新或跳转时提示保存
Nov 02 Javascript
你不可不知的Vue.js列表渲染详解
Oct 01 Javascript
微信小程序如何获取地址
Dec 24 Javascript
微信小程序 定位到当前城市实现实例代码
Feb 23 #Javascript
JavaScript中值类型和引用类型的区别
Feb 23 #Javascript
canvas绘制环形进度条
Feb 23 #Javascript
微信小程序 两种为对象属性赋值的方式详解
Feb 23 #Javascript
js实现文字跑马灯效果
Feb 23 #Javascript
微信小程序 出现错误:{"baseresponse":{"errcode":-80002,"errmsg":""}}解决办法
Feb 23 #Javascript
JS正则表达式验证密码格式的集中情况总结
Feb 23 #Javascript
You might like
十天学会php之第七天
2006/10/09 PHP
php Smarty 字符比较代码
2011/02/27 PHP
php产生随机数的两种方法实例代码 输出随机IP
2011/04/08 PHP
PHP多例模式介绍
2013/06/24 PHP
PHP简单检测网址是否能够正常打开的方法
2016/09/04 PHP
PHP使用gearman进行异步的邮件或短信发送操作详解
2020/02/27 PHP
Jquery中显示隐藏的实现代码分析
2011/07/26 Javascript
JavaScript中的6种运算符总结
2014/10/16 Javascript
jQuery学习笔记之基础中的基础
2015/01/19 Javascript
JS实现左右无缝轮播图代码
2016/05/01 Javascript
jQuery解决input元素的blur事件和其他非表单元素的click事件冲突问题
2016/08/15 Javascript
Vue.js 父子组件通讯开发实例
2016/09/06 Javascript
详解使用Vue.Js结合Jquery Ajax加载数据的两种方式
2017/01/10 Javascript
ES6中Array.copyWithin()函数的用法实例详解
2017/09/16 Javascript
微信小程序支付之c#后台实现方法
2017/10/19 Javascript
EasyUI实现下拉框多选功能
2017/11/07 Javascript
Vue不能观察到数组length的变化
2018/06/08 Javascript
Vue框架下引入ActiveX控件的问题解决
2019/03/25 Javascript
微信小程序如何修改本地缓存key中单个数据的详解
2019/04/26 Javascript
vue配置接口域名方法总结
2019/05/12 Javascript
JavaScript实现简单贪吃蛇效果
2020/03/09 Javascript
浅析Python多线程下的变量问题
2015/04/28 Python
python属于软件吗
2020/06/18 Python
利用CSS3伪元素实现逐渐发光的方格边框
2017/05/07 HTML / CSS
美国当红的名品折扣网:Gilt Groupe
2016/08/15 全球购物
世界上最大的街头服饰网站:Karmaloop
2017/02/04 全球购物
FC-Moto瑞典:欧洲最大的摩托车服装和头盔商店之一
2018/11/27 全球购物
数据库设计的包括哪两种,请分别进行说明
2016/07/15 面试题
先进事迹报告会感言
2014/01/24 职场文书
职业生涯规划书范文
2014/03/10 职场文书
国际商贸专业自荐信
2014/06/09 职场文书
四川省传达学习贯彻党的群众路线教育实践活动总结大会精神新闻稿
2014/10/26 职场文书
预备党员入党感想
2015/08/10 职场文书
python异步的ASGI与Fast Api实现
2021/07/16 Python
JavaScript事件的委托(代理)的用法示例详解
2022/02/18 Javascript
PyCharm 配置SSH和SFTP连接远程服务器
2022/05/11 Python