安装javascript
$ npm install mysql
简介html
这个一个mysql的nodejs版本的驱动,是用JavaScript来编写的。不须要编译java
这儿有个例子来示范如何使用:node
var mysql = require('mysql'); var connection = mysql.createConnection({ host : 'localhost', user : 'me', password : 'secret', database : 'my_db' }); connection.connect(); connection.query('SELECT 1 + 1 AS solution', function(err, rows, fields) { if (err) throw err; console.log('The solution is: ', rows[0].solution); }); connection.end();
从上面的例子,你能够学到:mysql
1.connection每个方法的调用都是被排队的,并且被顺序执行的git
2.用 connection 的end 方法来关闭一个connection。 此方法会在结束前确保那些遗留的query被执行。以后才会发送一个quit packet 给mysql server 端github
创建链接sql
推荐的创建链接的方法以下:数据库
var mysql = require('mysql'); var connection = mysql.createConnection({ host : 'example.org', user : 'bob', password : 'secret' }); connection.connect(function(err) { if (err) { console.error('error connecting: ' + err.stack); return; } console.log('connected as id ' + connection.threadId); });
另外,connection的打开也能够被一个query方法隐式的打开npm
var mysql = require('mysql'); var connection = mysql.createConnection(...); connection.query('SELECT 1', function(err, rows) { // connected! (unless `err` is set) });
任何类型的链接错误(握手或者网络)都是致命的。绝大多数错误是Error 对象的实例,另外error 有2个典型的属性
1.err.code :Mysql server error (ER_ACCESS_DENIED_ERROR),获取一个nodejs error ECONNREFUSED ,获取一个网络error PROTOCOL_CONNECTION_LOST
2.err.fatal : boolean类型,这个值指示这个error是否终止一个connection链接。假如这个error不是一个mysql protocol的错误,这个值应该是 not be defined
致命的error(fatal)是要被传播到全部的回调函数。以下:
var connection = require('mysql').createConnection({ port: 84943, // WRONG PORT }); connection.connect(function(err) { console.log(err.code); // 'ECONNREFUSED' console.log(err.fatal); // true }); connection.query('SELECT 1', function(err) { console.log(err.code); // 'ECONNREFUSED' console.log(err.fatal); // true });
正常状况下的error 仅仅被委托到他属于的回调函数。以下:
connection.query('USE name_of_db_that_does_not_exist', function(err, rows) { console.log(err.code); // 'ER_BAD_DB_ERROR' }); connection.query('SELECT 1', function(err, rows) { console.log(err); // null console.log(rows.length); // 1 });
最后,若是一个致命的错误,或者一个正常的错误,没有任何回调函数来处理,那么connection 对象的error 事件将被 emit。
connection.on('error', function(err) { console.log(err.code); // 'ER_BAD_DB_ERROR' }); connection.query('USE name_of_db_that_does_not_exist');
注意:'error' events,在node是很特别的,假如一个error发生,并且没有任何函数来处理他,那么堆栈信息将会被打印,进程将被kill。
链接参数
当创建一个链接,你能够下面的参数
connection.config.queryFormat = function (query, values) { if (!values) return query; return query.replace(/\:(\w+)/g, function (txt, key) { if (values.hasOwnProperty(key)) { return this.escape(values[key]); } return txt; }.bind(this)); }; connection.query("UPDATE posts SET title = :title", { title: "Hello MySQL" });
另外除了以对象的形式传送这些信息,也能够使用字符串形式,以下:
var connection = mysql.createConnection('mysql://user:pass@host/db?debug=true&charset=BIG5_CHINESE_CI&timezone=-0700');
终止链接
终止链接有两种方式,比较优雅的方式是调用end方法。
connection.end(function(err) { // The connection is terminated now });
假如在end的时候发生了致命的错误,err对象会在回调函数中启用,可是connection都会被终止。
另一种方式是destory 方法,这将当即终端socket链接,destory 也没有任何的事件和回调函数。
链接池
一个一个的建立和管理链接比较费事,mysql模块提供了链接池。
var mysql = require('mysql'); var pool = mysql.createPool({ connectionLimit : 10, host : 'example.org', user : 'bob', password : 'secret', database : 'my_db' }); pool.query('SELECT 1 + 1 AS solution', function(err, rows, fields) { if (err) throw err; console.log('The solution is: ', rows[0].solution); });
链接池比分享单个链接和管理多个链接更加的简单
var mysql = require('mysql'); var pool = mysql.createPool({ host : 'example.org', user : 'bob', password : 'secret', database : 'my_db' }); pool.getConnection(function(err, connection) { // connected! (unless `err` is set) });
当用一个connection完成操做时,仅仅须要调用connection.release()方法。connection 将会回到链接池中,准备下次链接
var mysql = require('mysql'); var pool = mysql.createPool(...); pool.getConnection(function(err, connection) { // Use the connection connection.query( 'SELECT something FROM sometable', function(err, rows) { // And done with the connection. connection.release(); // Don't use the connection here, it has been returned to the pool. }); });
假如你想关闭这个链接和从链接池中移除这个链接,请调用destroy方法,链接池将在下次调用的时候建立新的链接。
链接池建立链接是懒加载的,假如你配置了100个链接上限,而你仅仅只用到了5个,那么只有5个链接会被建立。链接池每次从队列的顶部拿链接,release 以后的链接放在底部。
链接池参数
与建立链接时的参数相同,不过有一些额外的:
链接池事件
1.connetion:链接池将emit 一个connection event,当一个新的链接被建立。
pool.on('connection', function (connection) { connection.query('SET SESSION auto_increment_increment=1') });
2.enqueue:链接池将emit 一个enqueue 事件,当一个connection 入栈
pool.on('enqueue', function () { console.log('Waiting for available connection slot'); });
关闭链接池中全部的链接
当链接池结束使用,或者shutdown server 时候
pool.end(function (err) { // all connections in the pool have ended });
这个回调函数,将在全部的query 执行以后被调用。 end 函数一旦被调用,pool.getConnetcion 将不在被执行
链接池集群
todo....
切换用户和改变当前的链接状态
mysql 提供一个改变用户和其余链接属性的命令,且不用shut down 当前的socket
connection.changeUser({user : 'john'}, function(err) { if (err) throw err; });
参数:
查询
最基本的方式来建立一个查询时调用.query方法(connection,pool等)
1.简易的
connection.query('SELECT * FROM `books` WHERE `author` = "David"', function (error, results, fields) { // error will be an Error if one occurred during the query // results will contain the results of the query // fields will contain information about the returned results fields (if any) });
2..query(sqlString, values, callback)
connection.query('SELECT * FROM `books` WHERE `author` = ?', ['David'], function (error, results, fields) { // error will be an Error if one occurred during the query // results will contain the results of the query // fields will contain information about the returned results fields (if any) });
3..query(options, callback)
connection.query({ sql: 'SELECT * FROM `books` WHERE `author` = ?', timeout: 40000, // 40s values: ['David'] }, function (error, results, fields) { // error will be an Error if one occurred during the query // results will contain the results of the query // fields will contain information about the returned results fields (if any) });
编码查询的参数
为了不sql的注入攻击,应该为任何一个用户输入的值进行编码,你能够用mysql.escape(). connection.escape() pool.escape().
var userId = 'some user provided value'; var sql = 'SELECT * FROM users WHERE id = ' + connection.escape(userId); connection.query(sql, function(err, results) { // ... });
另外你能够用 ? 字符来替换你所提供的参数
connection.query('SELECT * FROM users WHERE id = ?', [userId], function(err, results) { // ... });
connection.query('UPDATE users SET foo = ?, bar = ?, baz = ? WHERE id = ?', ['a', 'b', 'c', userId], function(err, results) { // ... });
不单单是 ? 替换。以下有各类状况也会发生编码:
['a', 'b'] 转成
'a', 'b'
[['a', 'b'], ['c', 'd']]
转成 ('a', 'b'), ('c', 'd')
能够有这样优雅的实现
var post = {id: 1, title: 'Hello MySQL'}; var query = connection.query('INSERT INTO posts SET ?', post, function(err, result) { // Neat! }); console.log(query.sql); // INSERT INTO posts SET `id` = 1, `title` = 'Hello MySQL'
编码查询标识
假如你不信任一个查询标识(database,table,column).由于标识可能来自于用户。你应该编码这些标识,用mysql.escapeId(),connection.escapeId(),pool.escapeId().
var sorter = 'date'; var sql = 'SELECT * FROM posts ORDER BY ' + connection.escapeId(sorter); connection.query(sql, function(err, results) { // ... });
var sorter = 'date'; var sql = 'SELECT * FROM posts ORDER BY ' + connection.escapeId('posts.' + sorter); // -> SELECT * FROM posts ORDER BY `posts`.`date`
假如想编码 . 这个字符,把第二个参数设置成true。
var sorter = 'date.2'; var sql = 'SELECT * FROM posts ORDER BY ' + connection.escapeId(sorter, true);
另外能够用 ?? 字符来替换标识,
var userId = 1; var columns = ['username', 'email']; var query = connection.query('SELECT ?? FROM ?? WHERE id = ?', [columns, 'users', userId], function(err, results) { // ... }); console.log(query.sql); // SELECT `username`, `email` FROM `users` WHERE id = 1
预查询
var sql = "SELECT * FROM ?? WHERE ?? = ?"; var inserts = ['users', 'id', userId]; sql = mysql.format(sql, inserts);
自定义查询格式化
connection.config.queryFormat = function (query, values) { if (!values) return query; return query.replace(/\:(\w+)/g, function (txt, key) { if (values.hasOwnProperty(key)) { return this.escape(values[key]); } return txt; }.bind(this)); }; connection.query("UPDATE posts SET title = :title", { title: "Hello MySQL" });
获取刚插入行的ID
假如你正在插入一个表,且这个表有个自增加的ID,你能取到这个ID,以下:
connection.query('INSERT INTO posts SET ?', {title: 'test'}, function(err, result) { if (err) throw err; console.log(result.insertId); });
获取受影响的行数
insert, update or delete
connection.query('DELETE FROM posts WHERE title = "wrong"', function (err, result) { if (err) throw err; console.log('deleted ' + result.affectedRows + ' rows'); })
获取改变的行数
update语句,他不统计那些没有改变值的行
connection.query('UPDATE posts SET ...', function (err, result) { if (err) throw err; console.log('changed ' + result.changedRows + ' rows'); })
流式查询
大数据量时,要分包处理
var query = connection.query('SELECT * FROM posts'); query .on('error', function(err) { // Handle error, an 'end' event will be emitted after this as well }) .on('fields', function(fields) { // the field packets for the rows to follow }) .on('result', function(row) { // Pausing the connnection is useful if your processing involves I/O connection.pause(); processRow(row, function() { connection.resume(); }); }) .on('end', function() { // all rows have been received });
注意:
多条数据查询
默认是关闭的,若是要开启这个功能,须要在connection 选项中开启 multipleStatements: true
一旦开启,能够这么查询:
connection.query('SELECT 1; SELECT 2', function(err, results) { if (err) throw err; // `results` is an array with one element for every statement in the query: console.log(results[0]); // [{1: 1}] console.log(results[1]); // [{2: 2}] });
流式查询
var query = connection.query('SELECT 1; SELECT 2'); query .on('fields', function(fields, index) { // the fields for the result rows that follow }) .on('result', function(row, index) { // index refers to the statement this result belongs to (starts at 0) });
假如报错了,err.index 属性将告诉你哪一个sql语句出错了。mysql 将不会执行下面的语句。
流式的多语句查询是实验性的。
Join 查询
当遇到多表链接的join查询,针对column名相同的状况这么处理
var options = {sql: '...', nestTables: true}; connection.query(options, function(err, results) { /* results will be an array like this now: [{ table1: { fieldA: '...', fieldB: '...', }, table2: { fieldA: '...', fieldB: '...', }, }, ...] */ });
var options = {sql: '...', nestTables: '_'}; connection.query(options, function(err, results) { /* results will be an array like this now: [{ table1_fieldA: '...', table1_fieldB: '...', table2_fieldA: '...', table2_fieldB: '...', }, ...] */ });
事务
connection.beginTransaction(function(err) { if (err) { throw err; } connection.query('INSERT INTO posts SET title=?', title, function(err, result) { if (err) { return connection.rollback(function() { throw err; }); } var log = 'Post ' + result.insertId + ' added'; connection.query('INSERT INTO log SET data=?', log, function(err, result) { if (err) { return connection.rollback(function() { throw err; }); } connection.commit(function(err) { if (err) { return connection.rollback(function() { throw err; }); } console.log('success!'); }); }); }); });
ping
一个ping包经过connection 发送给服务器
connection.ping(function (err) { if (err) throw err; console.log('Server responded to ping'); })
mysql To JavaScript 类型转化
NUMBER:
自定义类型转化
connection.query({ sql: '...', typeCast: function (field, next) { if (field.type == 'TINY' && field.length == 1) { return (field.string() == '1'); // 1 = true, 0 = false } return next(); } });