從零學(xué)習(xí)node.js之mysql數(shù)據(jù)庫(kù)的操作(五)
準(zhǔn)備工作
在使用node操作mysql數(shù)據(jù)庫(kù)時(shí),需要先下載mysql模塊:
npm install mysql --save-dev
在引入mysql模塊后,就可以進(jìn)行數(shù)據(jù)庫(kù)的連接和其他的操作了。
// test.js var mysql = require('mysql');
一、連接數(shù)據(jù)庫(kù)
首先保證本地已經(jīng)安裝數(shù)據(jù)庫(kù),并已正常啟動(dòng),然后開(kāi)始進(jìn)行連接:
// test.js var mysql = require('mysql'); // 創(chuàng)建連接 var conn = mysql.createConnection({ host : '127.0.0.1', user : 'root', password : '123', database : 'test' }); // 創(chuàng)建連接后不論是否成功都會(huì)調(diào)用 conn.connect(function(err){ if(err) throw err; console.log('connect success!'); }); // 其他的數(shù)據(jù)庫(kù)操作,位置預(yù)留 // 關(guān)閉連接時(shí)調(diào)用 conn.end(function(err){ if(err) throw err; console.log('connect end'); })
執(zhí)行node test.js
后,就會(huì)輸出:
$ node test.js connect success! connect end
連接成功,然后連接關(guān)閉。這就說(shuō)明程序可以正常連接數(shù)據(jù)庫(kù)了,然后就開(kāi)始進(jìn)行增刪改查的操作。
二、CURD
比如我們有這樣的一個(gè)user表,里面有4個(gè)字段,其中uid是自增字段:
uid | username | password | |
1 | meizi | meizi | 123@qq.com |
2 | test | test | 456@qq.com |
我們就對(duì)這個(gè)表進(jìn)行增刪改查的操作。
mysql中有個(gè)query方法可以用來(lái)執(zhí)行任意正確的sql語(yǔ)句,然后在回調(diào)函數(shù)里給出執(zhí)行sql語(yǔ)句后的結(jié)果。query方法是異步執(zhí)行的,若并列書(shū)寫(xiě)多個(gè)query方法的話,是不能按照書(shū)寫(xiě)順序依次阻塞式執(zhí)行的。
2.1 查詢
使用最普遍最多的就是查詢操作了。
conn.query('SELECT * FROM `user`', function(err, result, fields){ if(err) throw err; console.log(result); }); console.log( 'select ended!' );
輸出的結(jié)果:
select ended! // 先輸出 [ RowDataPacket { uid: 1, username: 'meizi', password: 'meizi email: '123@qq.com' }, RowDataPacket { uid: 2, username: 'test', password: 'test email: '456@qq.com' } ]
可以看到,結(jié)果集是一個(gè)數(shù)組,數(shù)組中的每條數(shù)據(jù)都是一個(gè)RowDataPacket對(duì)象,在使用時(shí),可以像json對(duì)象一樣獲取數(shù)據(jù),也可以使用JSON.stringify
把result轉(zhuǎn)換為json字符串,但是,result并不是JSON數(shù)據(jù)。而且,即使結(jié)果集中只有一條數(shù)據(jù),也是以數(shù)組的形式返回的。
console.log(result[0].username); // meizi
輸出即為字符串類型的數(shù)據(jù)。
2.2 添加
向數(shù)據(jù)庫(kù)中添加數(shù)據(jù)使用的是INSERT,INSERT語(yǔ)句有兩種形式都可以使用:
第1種,先列好要插入的數(shù)據(jù)對(duì)應(yīng)的字段,然后跟上數(shù)據(jù)(如果要給所有的字段都插入數(shù)據(jù),可以省略字段不寫(xiě),但是數(shù)據(jù)的書(shū)寫(xiě)順序要跟數(shù)據(jù)表里的字段一一對(duì)應(yīng)):
INSERT INTO table_name ( field1, field2,...fieldN ) VALUES ( value1, value2,...valueN );
第2種,可以像update操作一樣書(shū)寫(xiě),將field與value對(duì)應(yīng)的更緊密:
INSERT INTO table_name SET field1=value1, field2=value2, ... fieldN=valueN;
我更加喜歡第2種方式,這種方式更能看出操作了哪些字段,看出字段和數(shù)據(jù)的對(duì)應(yīng)關(guān)系。在node中插入數(shù)據(jù):
conn.query("INSERT INTO `user` SET `username`='qwerty', `password`='741', `email`='qwerty@qq.com'", function(err, result){ if(err) throw err; console.log(result); });
插入數(shù)據(jù)后返回的結(jié)果是:
OkPacket { fieldCount: 0, affectedRows: 1, insertId: 4, // 數(shù)據(jù)插入成功時(shí),對(duì)應(yīng)的主鍵id serverStatus: 2, warningCount: 0, message: '', protocol41: true, changedRows: 0 }
affectedRows表示數(shù)據(jù)表中受影響的行數(shù),數(shù)據(jù)插入成功則為1,失敗則為0;在主鍵自增的情況下,insertId是數(shù)據(jù)插入成功后對(duì)應(yīng)的主鍵id,如果主鍵不自增,則insertId為0。
2.3 更新
使用update語(yǔ)句更新數(shù)據(jù):
// 更新uid的密碼 conn.query('UPDATE `user` SET `password`="123456" WHERE `uid`=4', function(err, result){ if(err) throw err; console.log(result); });
輸出的結(jié)果:
OkPacket { fieldCount: 0, affectedRows: 1, insertId: 0, serverStatus: 2, warningCount: 0, message: '(Rows matched: 1 Changed: 1 Warnings: 0', protocol41: true, changedRows: 1 }
可以看到輸出結(jié)果的類型與插入數(shù)據(jù)時(shí)輸出結(jié)果的類型是一樣的。我們分析一下,執(zhí)行sql語(yǔ)句后,有3種結(jié)果:
- 成功修改數(shù)據(jù): affectedRows:1, changedRows:1
- 要修改的數(shù)據(jù)與原數(shù)據(jù)相同: affectedRows:1, changedRows:0
- 未找到需要修改的數(shù)據(jù): affectedRows:0, changedRows:0
因此可以根據(jù)這兩個(gè)字段輸出相應(yīng)的結(jié)果。
2.4 刪除
使用delete語(yǔ)句刪除語(yǔ)句:
conn.query('DELETE FROM `user` WHERE `uid`=4', function(err, result, fields){ if(err) throw err; console.log(result); });
輸出的結(jié)果:
OkPacket { fieldCount: 0, affectedRows: 1, insertId: 0, serverStatus: 2, warningCount: 0, message: '', protocol41: true, changedRows: 0 }
刪除成功,則affectedRows為1,刪除的數(shù)據(jù)不存在,則為0。
三、連接池
數(shù)據(jù)庫(kù)連接是一種有限的,能夠顯著影響到整個(gè)應(yīng)用程序的伸縮性和健壯性的資源,在多用戶的網(wǎng)頁(yè)應(yīng)用程序中體現(xiàn)得尤為突出。
數(shù)據(jù)庫(kù)連接池正是針對(duì)這個(gè)問(wèn)題提出來(lái)的,它會(huì)負(fù)責(zé)分配、管理和釋放數(shù)據(jù)庫(kù)連接,允許應(yīng)用程序重復(fù)使用一個(gè)現(xiàn)有的數(shù)據(jù)庫(kù)連接,而不是重新建立一個(gè)連接,釋放空閑時(shí)間超過(guò)最大允許空閑時(shí)間的數(shù)據(jù)庫(kù)連接以避免因?yàn)檫B接未釋放而引起的數(shù)據(jù)庫(kù)連接遺漏。
3.1 創(chuàng)建連接池
使用mysql.createPool()
可創(chuàng)建連接池:
// 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()
可以共享一個(gè)連接,或管理多個(gè)連接。
// 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); }) });
連接使用完后通過(guò)調(diào)用connection.release()
方法可以將連接返回到連接池中,這個(gè)連接可以被其它人重復(fù)使用:
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(); // 接下來(lái)connection已經(jīng)無(wú)法使用,它已經(jīng)被返回到連接池中 }) });
可以使用connection.destroy()
徹底銷毀連接。
3.2 連接池事件
createPool()
方法會(huì)返回一個(gè)連接池實(shí)例對(duì)象,這個(gè)對(duì)象中有一些事件。
connection
連接池中產(chǎn)生新連接時(shí)會(huì)發(fā)送'connection'事件:
pool.on('connection', function (connection) { console.log('new connection'); });
3.3 QUERY與GETCONNECTION的區(qū)別
這兩個(gè)方法都能進(jìn)行操作,那么這兩者有什么區(qū)別呢?
在pool.getConnection
中的connection在其回調(diào)函數(shù)里是一直的,可以保證這一系列的操作都是在同一個(gè)connection中執(zhí)行的;pool.query
則每次執(zhí)行時(shí)可能會(huì)在不同的connection中執(zhí)行,可能會(huì)得到意想不到的結(jié)果。
比如SQL_CALC_FOUND_ROWS
和FOUND_ROWS
這需要兩個(gè)sql語(yǔ)句完成,是獲取檢索行的數(shù)目。
pool.query('SELECT SQL_CALC_FOUND_ROWS * FROM `user`'); pool.query('SELECT FOUND_ROWS()');
這兩個(gè)可能在不同的connection中執(zhí)行,第2個(gè)sql語(yǔ)句返回的就不是上一個(gè)sql語(yǔ)句的結(jié)果了。
四、sql防注入
sql防注入的關(guān)鍵就是不能直接把數(shù)據(jù)拼接到sql語(yǔ)句中,必須得對(duì)數(shù)據(jù)進(jìn)行轉(zhuǎn)義,或者使用提供的方法拼接sql語(yǔ)句。這里主要有四種方法可以使用。
4.1 使用ESCAPE()對(duì)參數(shù)進(jìn)行編碼
參數(shù)編碼方法有:mysql.escape()/connection.escape()/pool.escape()
,這三個(gè)方法可以在你需要的時(shí)候調(diào)用:
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); })
對(duì)雙引號(hào)進(jìn)行了安全轉(zhuǎn)義。
escapeId()
可以對(duì)不信任的表名,字段名進(jìn)行轉(zhuǎn)義。
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
同時(shí),escape()
的編碼規(guī)則如下:
- Numbers不進(jìn)行轉(zhuǎn)換
- Booleans轉(zhuǎn)換為true/false
- Date對(duì)象轉(zhuǎn)換為'YYYY-mm-dd HH:ii:ss'字符串
- Buffers轉(zhuǎn)換為hex字符串,如X'0fa5'
- Strings進(jìn)行安全轉(zhuǎn)義
- Arrays轉(zhuǎn)換為列表,如[‘a(chǎn)', ‘b']會(huì)轉(zhuǎn)換為'a', ‘b'
- 多維數(shù)組轉(zhuǎn)換為組列表,如[[‘a(chǎn)', ‘b'], [‘c', ‘d']]會(huì)轉(zhuǎn)換為(‘a(chǎn)', ‘b'), (‘c', ‘d')
- Objects會(huì)轉(zhuǎn)換為key=value鍵值對(duì)的形式。嵌套的對(duì)象轉(zhuǎn)換為字符串
- undefined/null會(huì)轉(zhuǎn)換為NULL
- MySQL不支持NaN/Infinity,并且會(huì)觸發(fā)MySQL錯(cuò)誤
4.2 占位符
可以使用?作為參數(shù)占位符。在使用查詢參數(shù)占位符時(shí),在其內(nèi)部自動(dòng)調(diào)用 connection.escape()
方法對(duì)傳入?yún)?shù)進(jìn)行編碼。
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'
同時(shí),如果執(zhí)行添加或更新操作時(shí),還可以這樣寫(xiě):
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'
數(shù)據(jù)庫(kù)中的表明和字段名,可以使用??作為占位符,在拼接完成后會(huì)自動(dòng)添加上``:
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()轉(zhuǎn)義參數(shù)
不多說(shuō),樣例如下:
var userId = 1; var sql = "SELECT * FROM ?? WHERE ?? = ?"; var inserts = ['user', 'uid', userId]; sql = mysql.format(sql, inserts); // SELECT * FROM `user` WHERE `uid` = 1
五、多語(yǔ)句查詢
出于安全考慮node-mysql默認(rèn)禁止多語(yǔ)句查詢(可以防止SQL注入),啟用多語(yǔ)句查詢可以將multipleStatements選項(xiàng)設(shè)置為true:
var connection = mysql.createConnection({multipleStatements: true});
啟用后可以在一個(gè)query查詢中執(zhí)行多條語(yǔ)句:
connection.query('SELECT 1; SELECT 2', function(err, results) { if (err) throw err; // `results`是一個(gè)包含多個(gè)語(yǔ)句查詢結(jié)果的數(shù)組 console.log(results[0]); console.log(results[1]); });
總結(jié)
本節(jié)只是總結(jié)了node對(duì)mysql數(shù)據(jù)庫(kù)的各種操作,但如果實(shí)際應(yīng)用起來(lái)的話,還遠(yuǎn)遠(yuǎn)不夠。努力學(xué)習(xí)中…希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,如果有疑問(wèn)大家可以留言交流。
- node.js 開(kāi)發(fā)指南 – Node.js 連接 MySQL 并進(jìn)行數(shù)據(jù)庫(kù)操作
- Node.js數(shù)據(jù)庫(kù)操作之查詢MySQL數(shù)據(jù)庫(kù)(二)
- Node.js下向MySQL數(shù)據(jù)庫(kù)插入批量數(shù)據(jù)的方法
- Node.js操作mysql數(shù)據(jù)庫(kù)增刪改查
- Node.js數(shù)據(jù)庫(kù)操作之連接MySQL數(shù)據(jù)庫(kù)(一)
- node.js平臺(tái)下的mysql數(shù)據(jù)庫(kù)配置及連接
- Linux下為Node.js程序配置MySQL或Oracle數(shù)據(jù)庫(kù)的方法
- Node.js實(shí)現(xiàn)連接mysql數(shù)據(jù)庫(kù)功能示例
- Node.js對(duì)MySQL數(shù)據(jù)庫(kù)的增刪改查實(shí)戰(zhàn)記錄
- node.js如何操作MySQL數(shù)據(jù)庫(kù)
- Node.js實(shí)現(xiàn)http請(qǐng)求服務(wù)與Mysql數(shù)據(jù)庫(kù)操作方法詳解
- node.js對(duì)于數(shù)據(jù)庫(kù)MySQL基本操作實(shí)例總結(jié)【增刪改查】
相關(guān)文章
解決Window10系統(tǒng)下Node安裝報(bào)錯(cuò)的問(wèn)題分析
今天電腦重裝了win10系統(tǒng),在安裝Node的過(guò)程中出現(xiàn)了下面的問(wèn)題,下面就和大家分享下用來(lái)解決這種問(wèn)題的小方法2016-12-12Nodejs使用fs-extra模塊進(jìn)行目錄和文件操作用法示例
fs-extra模塊是基于fs?的文件操作相關(guān)工具庫(kù),封裝了一些fs實(shí)現(xiàn)起來(lái)相對(duì)復(fù)雜的工具,下面這篇文章主要給大家介紹了關(guān)于Nodejs使用fs-extra模塊進(jìn)行目錄和文件操作用法的相關(guān)資料,需要的朋友可以參考下2024-06-06Node.js的特點(diǎn)和應(yīng)用場(chǎng)景介紹
這篇文章主要介紹了Node.js的特點(diǎn)和應(yīng)用場(chǎng)景介紹,本文講解了Node.js的異步I/O、 事件循環(huán)與回調(diào)函數(shù)、單線程、 跨平臺(tái)等特性,然后總結(jié)了它的使用場(chǎng)景,需要的朋友可以參考下2014-11-11關(guān)于node.js版本npm -v報(bào)錯(cuò)問(wèn)題的解決方法
最近工作中遇到了些問(wèn)題,這里總結(jié)下,下面這篇文章主要給大家介紹了關(guān)于node.js版本npm -v報(bào)錯(cuò)問(wèn)題的解決方法,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-04-04node.js中的path.join方法使用說(shuō)明
這篇文章主要介紹了node.js中的path.join方法使用說(shuō)明,本文介紹了path.join的方法說(shuō)明、語(yǔ)法、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下2014-12-12修改node.js默認(rèn)的npm安裝目錄實(shí)例
今天小編就為大家分享一篇修改node.js默認(rèn)的npm安裝目錄實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-05-05