安裝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(); } });