這篇文章主要介紹了node操作數據庫,結合實例形式較為詳細的分析了node操作數據庫的連接、增刪改查、事務處理及錯誤處理相關操作技巧,需要的朋友可以參考下
本文實例講述了node操作mysql數據庫的方法。分享給大家供大家參考,具體如下:
1、建立數據庫連接:createConnection()方法
該方法接受一個對象作為參數,該對象有四個常用的屬性host,user,password,database。與php中鏈接數據庫的參數相同。屬性列表如下:
host | 連接數據庫所在的主機名. (默認: localhost) |
port | 連接端口. (默認: 3306) |
localAddress | 用于TCP連接的IP地址. (可選) |
socketPath | 鏈接到unix域的路徑。在使用host和port時該參數會被忽略. |
user | MySQL用戶的用戶名. |
password | MySQL用戶的密碼. |
database | 鏈接到的數據庫名稱 (可選). |
charset | 連接的字符集. (默認: ‘UTF8_GENERAL_CI’.設置該值要使用大寫!) |
timezone | 儲存本地時間的時區. (默認: ‘local’) |
stringifyObjects | 是否序列化對象. See issue #501. (默認: ‘false’) |
insecureAuth | 是否允許舊的身份驗證方法連接到數據庫實例. (默認: false) |
typeCast | 確定是否講column值轉換為本地Javascript類型列值. (默認: true) |
queryFormat | 自定義的查詢語句格式化函數. |
supportBigNumbers | 數據庫處理大數字(長整型和含小數),時應該啟用 (默認: false). |
bigNumberStrings | 啟用 supportBigNumbers和bigNumberStrings 并強制這些數字以字符串的方式返回(默認: false). |
dateStrings | 強制日期類型(TIMESTAMP, DATETIME, DATE)以字符串返回,而不是一javascript Date對象返回. (默認: false) |
debug | 是否開啟調試. (默認: false) |
multipleStatements | 是否允許在一個query中傳遞多個查詢語句. (Default: false) |
flags | 鏈接標志. |
var?connection?=?mysql.createConnection('mysql://user:pass@host/db?debug=true&charset=BIG5_CHINESE_CI&timezone=-0700');
2、結束數據庫連接end()和destroy()
end()接受一個,并且會在query結束之后才觸發,如果query出錯,仍然會終止鏈接,錯誤會傳遞到回調函數中處理。
destroy()立即終止數據庫連接,即使還有query沒有完成,之后的回調函數也不會在觸發。
3、創建連接池 createPool(Object)? Object和createConnection參數相同。
pool.on('connection',?function(connection)?{ ?connection.query('SET?SESSION?auto_increment_increment=1') });
connection.release()釋放鏈接到連接池。如果需要關閉連接并且刪除,需要使用connection.destroy()
pool除了接受和connection相同的參數外,還接受幾個擴展的參數
createConnection | 用于創建鏈接的函數. (Default: mysql.createConnection) |
waitForConnections | 決定當沒有連接池或者鏈接數打到最大值時pool的行為. 為true時鏈接會被放入隊列中在可用是調用,為false時會立即返回error. (Default: true) |
connectionLimit | 最大連接數. (Default: 10) |
queueLimit | 連接池中連接請求的烈的最大長度,超過這個長度就會報錯,值為0時沒有限制. (Default: 0) |
4、連接池集群
允許不同的host鏈接
//?create var?poolCluster?=?mysql.createPoolCluster(); poolCluster.add(config);?//?anonymous?group poolCluster.add('MASTER',?masterConfig); poolCluster.add('SLAVE1',?slave1Config); poolCluster.add('SLAVE2',?slave2Config); //?Target?Group?:?ALL(anonymous,?MASTER,?SLAVE1-2),?Selector?:?round-robin(default) poolCluster.getConnection(function?(err,?connection)?{}); //?Target?Group?:?MASTER,?Selector?:?round-robin poolCluster.getConnection('MASTER',?function?(err,?connection)?{}); //?Target?Group?:?SLAVE1-2,?Selector?:?order //?If?can't?connect?to?SLAVE1,?return?SLAVE2.?(remove?SLAVE1?in?the?cluster) poolCluster.on('remove',?function?(nodeId)?{ ???console.log('REMOVED?NODE?:?'?+?nodeId);?//?nodeId?=?SLAVE1 }); poolCluster.getConnection('SLAVE*',?'ORDER',?function?(err,?connection)?{}); //?of?namespace?:?of(pattern,?selector) poolCluster.of('*').getConnection(function?(err,?connection)?{}); var?pool?=?poolCluster.of('SLAVE*',?'RANDOM'); pool.getConnection(function?(err,?connection)?{}); pool.getConnection(function?(err,?connection)?{}); //?destroy poolCluster.end();
鏈接集群的可選參數
canRetry | 值為true時,允許連接失敗時重試(Default: true) |
removeNodeErrorCount | 當連接失敗時 errorCount 值會增加. 當errorCount 值大于 removeNodeErrorCount 將會從PoolCluster中刪除一個節點. (Default: 5) |
defaultSelector | 默認選擇器. (Default: RR) |
RR | 循環. (Round-Robin) |
RANDOM | 通過隨機函數選擇節點. |
ORDER | 無條件地選擇第一個可用節點. |
5、切換用戶/改變連接狀態
Mysql允許在比斷開連接的的情況下切換用戶
connection.changeUser({user?:?'john'},?function(err)?{ ?if?(err)?throw?err; });
參數
user | 新的用戶 (默認為早前的一個). |
password | 新用戶的新密碼 (默認為早前的一個). |
charset | 新字符集 (默認為早前的一個). |
database | 新數據庫名稱 (默認為早前的一個). |
6、處理服務器連接斷開
var?db_config?=?{ ??host:?'localhost', ??user:?'root', ??password:?'', ??database:?'example' }; var?connection; function?handleDisconnect()?{ ?connection?=?mysql.createConnection(db_config);?//?Recreate?the?connection,?since ?????????????????????????//?the?old?one?cannot?be?reused. ?connection.connect(function(err)?{???????//?The?server?is?either?down ??if(err)?{???????????????????//?or?restarting?(takes?a?while?sometimes). ???console.log('error?when?connecting?to?db:',?err); ???setTimeout(handleDisconnect,?2000);?//?We?introduce?a?delay?before?attempting?to?reconnect, ??}???????????????????//?to?avoid?a?hot?loop,?and?to?allow?our?node?script?to ?});???????????????????//?process?asynchronous?requests?in?the?meantime. ?????????????????????//?If?you're?also?serving?http,?display?a?503?error. ?connection.on('error',?function(err)?{ ??console.log('db?error',?err); ??if(err.code?===?'PROTOCOL_CONNECTION_LOST')?{?//?Connection?to?the?MySQL?server?is?usually ???handleDisconnect();?????????????//?lost?due?to?either?server?restart,?or?a ??}?else?{???????????????????//?connnection?idle?timeout?(the?wait_timeout ???throw?err;?????????????????//?server?variable?configures?this) ??} ?}); } handleDisconnect();
7、轉義查詢值
為了避免SQL注入攻擊,需要轉義用戶提交的數據。可以使用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)?{ ???//?... });
不同類型值的轉換結果
Numbers 不變
Booleans 轉換為字符串 ‘true’ / ‘false’
Date 對象轉換為字符串 ‘YYYY-mm-dd HH:ii:ss’
Buffers 轉換為是6進制字符串
Strings 不變
Arrays => [‘a’, ‘b’] 轉換為 ‘a’, ‘b’
嵌套數組 [[‘a’, ‘b’], [‘c’, ‘d’]] 轉換為 (‘a’, ‘b’), (‘c’, ‘d’)
Objects 轉換為 key = ‘val’ pairs. 嵌套對象轉換為字符串.
undefined / null ===> NULL
NaN / Infinity 不變. 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'
或者手動轉換
var?query?=?"SELECT?*?FROM?posts?WHERE?title="?+?mysql.escape("Hello?MySQL"); console.log(query);?//?SELECT?*?FROM?posts?WHERE?title='Hello?MySQL'
8、轉換查詢標識符
如果不能信任SQL標識符(數據庫名、表名、列名),可以使用轉換方法mysql.escapeId(identifier);
var?sorter?=?'date'; var?query?=?'SELECT?*?FROM?posts?ORDER?BY?'?+?mysql.escapeId(sorter); console.log(query);?//?SELECT?*?FROM?posts?ORDER?BY?`date`
支持轉義多個
var?sorter?=?'date'; var?query?=?'SELECT?*?FROM?posts?ORDER?BY?'?+?mysql.escapeId('posts.'?+?sorter); console.log(query);?//?SELECT?*?FROM?posts?ORDER?BY?`posts`.`date`
可以使用??作為標識符的占位符
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
9、準備查詢
可以使用mysql.format來準備查詢語句,該函數會自動的選擇合適的方法轉義參數。
var?sql?=?"SELECT?*?FROM????WHERE????=??"; var?inserts?=?['users',?'id',?userId]; sql?=?mysql.format(sql,?inserts);
10、自定義格式化函數
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"?});
11、獲取插入行的id
當使用自增主鍵時獲取插入行id,如:
connection.query('INSERT?INTO?posts?SET??',?{title:?'test'},?function(err,?result)?{ ???if?(err)?throw?err; ???console.log(result.insertId); ??});
12、流處理
有時你希望選擇大量的行并且希望在數據到達時就處理他們,你就可以使用這個方法
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 ???});
13、混合查詢語句(多語句查詢)
因為混合查詢容易被SQL注入攻擊,默認是不允許的,可以使用:
var?connection?=?mysql.createConnection({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) ???});
如果其中一個查詢語句出錯,Error對象會包含err.index指示錯誤語句的id,整個查詢也會終止。
混合查詢結果的流處理方式是做實驗性的,不穩定。
14、事務處理
connection級別的簡單事務處理
connection.beginTransaction(function(err)?{ ???if?(err)?{?throw?err;?} ???connection.query('INSERT?INTO?posts?SET?title=?',?title,?function(err,?result)?{ ????if?(err)?{ ?????connection.rollback(function()?{ ??????throw?err; ?????}); ????} ????var?log?=?'Post?'?+?result.insertId?+?'?added'; ????connection.query('INSERT?INTO?log?SET?data=?',?log,?function(err,?result)?{ ?????if?(err)?{ ??????connection.rollback(function()?{ ???????throw?err; ??????}); ?????} ?????connection.commit(function(err)?{ ??????if?(err)?{ ???????connection.rollback(function()?{ ????????throw?err; ???????}); ??????} ??????console.log('success!'); ?????}); ????}); ???}); ??});
15、錯誤處理
err.code?=?string err.fatal?=>?boolean