nodejs 訪問mysql


安裝

$ npm install mysql

簡介

這個一個mysql的nodejs版本的驅動,是用JavaScript來編寫的。不需要編譯

這兒有個例子來示范如何使用:

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();

 從上面的例子,你可以學到:

 1.connection每一個方法的調用都是被排隊的,而且被順序執行的

   2.用 connection 的end 方法來關閉一個connection。 此方法會在結束前確保那些遺留的query被執行。之后才會發送一個quit packet 給mysql server 端

建立連接

推薦的建立連接的方法如下:

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方法隱式的打開

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。

連接參數

當建立一個連接,你可以下面的參數

  • host:主機的名字,(默認:localhost)
  • port:主機端口號(默認3306)
  • localAddress:主機的ip地址(TCP連接,可選)
  • socketPath:主機是unix 的tcp連接地址,如果設置了host 和port,這個值被忽略
  • user:mysql 授權的用戶
  • password:mysql授權的用戶密碼
  • database:數據庫名稱
  • multipleStatements:多個查詢,(select 1,select 2. )。 處於安全考慮,默認false
  • charset:連接的字符編碼,(默認UTF8_GENERAL_CI)
  • timeZone:時區用來存儲本地日期,(默認local)
  • connectionTimeOut:連接超時 毫秒,(默認10000)
  • stringifyObjects:是否把對象字符串化(默認false)
  • typeCast:決定是否一個字段的值應該被轉化成一個原生的JavaScript類型的值(默認true)
  • queryFormat:自定義的query函數 
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" });
  • supportBigNumbers:當處理大的數據的時候應該開啟這個選項(默認false),比如在數據庫類型中的bitint 或者decimal
  • bigNumberStrings:同時啟用bigNumberStrings和supportBigNumbers 將強制大數據結構(Bigint 或者decimal)以JavaScript中的String Objects 返回。(默認值false)。如果supportBigNumbers禁止,此選項將被忽略。如果supportBigNumber開啟,此選項關閉,那么如果數字在 -2^53, +2^53 區間,那么返回Number Object 否則返回String Object。
  • dateStrings:強制數據庫中的(TIMESTAMP, DATETIME, DATE)轉化成字符串否則返回JavaScript Date類型(默認false)
  • debug:是否在控制台打印協議的信息(默認 false)
  • trace:在錯誤發生的時候打印堆棧信息,(默認true)
  • multipleStatements:待補充(默認false)
  • flag
  • ssl

另外除了以對象的形式傳送這些信息,也可以使用字符串形式,如下:

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 之后的連接放在底部。

連接池參數

與創建連接時的參數相同,不過有一些額外的:

  • acquireTimeout:默認10000毫秒。一個連接捕獲的超時時長,這跟connectionTimeout不同,因為獲得一個池連接並不總是涉及到連接.
  • waitForConnections:默認為true。 假如true。在連接池沒有連接可用或者連接已經達到上限的時候,連接池將立即返回並攜帶error參數。 假如false,連接池將排隊等待連接可用。
  • connectionLimit:默認10個。
  • queueLimit:連接請求的最大上線數,如果超過這個數,將返回error。如果設置成0,則表示無限制,默認0.

連接池事件

 

  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;
});

參數:

  • user
  • password
  • charset
  • database

查詢

最基本的方式來創建一個查詢時調用.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) {
  // ...
});

不僅僅是 ? 替換。如下有各種情況也會發生編碼:

  • 數字類型不受影響
  • Booleans 被 轉化成 true/false
  • 日期類型被轉化成YYYY-mm-dd HH:ii:ss
  • 字節類型,被轉化成16進制字符串,eg 0fa5
  • 數組被轉化成list,['a', 'b'] 轉成  'a', 'b'
  • 多重數組被轉成多重list,[['a', 'b'], ['c', 'd']] 轉成 ('a', 'b'), ('c', 'd')
  • 對象被轉化成 key=value 的形式,假如屬性值是fuction 就跳過,假如屬性值是object 就 調用 toString()方法
  • undefined/null 轉成 null
  • NAN/infinity Mysql不支持,如果插入會引發mysql 報錯

可以有這樣優雅的實現

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
  });

 注意:

  • pause() 方法是關閉流的閥門。
  • 不要為這種流式的查詢提供回調函數
  • 不要pause 時間過長,否則將遇到error,(The server close the connection)。這個時間有mysql 服務的 net_write_timeout setting 決定

多條數據查詢

默認是關閉的,如果要開啟這個功能,需要在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:

  • TINYINT
  • SMALLINT
  • INT
  • MEDIUMINT
  • YEAR
  • FLOAT
  • DOUBLE

Date

  • TIMESTAMP
  • DATE
  • DATETIME

Buffer

  • TINYBLOB
  • MEDIUMBLOB
  • LONGBLOB
  • BLOB
  • BINARY
  • VARBINARY
  • BIT (last byte will be filled with 0 bits as necessary)

String 

  • CHAR
  • VARCHAR
  • TINYTEXT
  • MEDIUMTEXT
  • LONGTEXT
  • TEXT
  • ENUM
  • SET

自定義類型轉化

connection.query({
  sql: '...',
  typeCast: function (field, next) {
    if (field.type == 'TINY' && field.length == 1) {
      return (field.string() == '1'); // 1 = true, 0 = false
    }
    return next();
  }
});

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM