koa訪問mysql數據庫操作
契機
用restify與express有一年多了,一直在考慮什么時候上koa與es6,看到 《一起學koa》 項目,決定以此為契機行動起來。首先我要完成的是對數據庫操作的封裝,將以前項目實踐中的代碼遷移到koa上來,于是決定先完成 《一起學koa》 中的mysql任務。
koa基本知識
koa依賴co,最新版基于promise實現。我們使用koa的時候都是通過use添加一個中間件,router也是一個個中間件,我們看一下use都做了什么?
app.use = function(fn){ this.middleware.push(fn); return this; };
它只是將參數保存起來,然后返回引用,最后由co完成調用,因此要求中間件中的異步調用都使用Promise形式。
實現方法一(co-mysql)
mysql庫是以回調形式實現的,而koa中間件要求Promise形式,經過搜索,發現了co-mysql和mysql-co,這兩個庫的思路差不多,mysql-co封裝度更高,并使用速度更快的mysql2,而co-mysql更簡單,只是將mysql.query封裝成Promise形式。下面是基于co-mysql的寫法
var wrapper = require('co-mysql'), mysql = require('mysql'); var options = { host : 'localhost', port : 3306 , database : 'test', user: 'root', password : 'rootroot' }; var pool = mysql.createPool(options), p = wrapper(pool); ... var rows = yield p.query('SELECT 1'); yield this.render('index', { title: rows[0].fieldName }); ... })();
實現方法二(promisify-node)
找到promisify-node庫,可以將庫整體轉化為Promise形式,示例代碼如下:
var promisify = require("promisify-node"); var db = promisify("myDbHelper"); ... var rows = yield db.getById('tableName', {id:1}); yield this.render('index', { title: rows[0].fieldName }); ...
實現方法三(thunkify、thunkify-wrap)
看tj/co說明的Yieldables部分說明如下:The yieldable objects currently supported are:
-
promises
-
thunks (functions)
-
array (parallel execution)
-
objects (parallel execution)
-
generators (delegation)
-
generator functions (delegation)
因此使用thunkify也能夠完成封裝,thunkify-wrap是一個增強版的thunkify,不過看說明,這種方法在未來的發展中可能會被淘汰,大概的使用如下:
var genify = require('thunkify-wrap').genify; var db = genify("myDbHelper"); ... var rows = yield db.getById('tableName', {id:1}); yield this.render('index', { title: rows[0].fieldName }); ...
實現方法四(直接方法)
直接改造原來express下的代碼為Promise形式,參考了co-mysql,并仔細學習了Promise相關知識,完成了已有代碼的改造,代碼及說明如下:dbHelper.js
var config = require('./dbconfig'); var options = { 'host': config.db_host, 'port': config.db_port, 'database': config.db_name, 'user': config.db_user, 'password': config.db_passwd } var mysql = require('mysql'); var pool = mysql.createPool(options); //內部對mysql的封裝,執行sql語句 function execQuery(sql, values, callback) { var errinfo; pool.getConnection(function(err, connection) { if (err) { errinfo = 'DB-獲取數據庫連接異常!'; throw errinfo; } else { var querys = connection.query(sql, values, function(err, rows) { release(connection); if (err) { errinfo = 'DB-SQL語句執行錯誤:' + err; callback(err); } else { callback(null,rows); //注意:第一個參數必須為null } }); } }); } function release(connection) { try { connection.release(function(error) { if (error) { console.log('DB-關閉數據庫連接異常!'); } }); } catch (err) {} } //對外接口返回Promise函數形式 exports.getById = function(tablename, id){ return new Promise(function(resolve, reject){ var values = {id:id}; var sql = 'select * from ?? where ?'; execQuery(sql,[tablename, values], function(err, rows){ if(err){ reject(err); }else{ resolve(rows); } }) }); }
routes/index.js
var db = require("../dbHelper"); ... var rows = yield db.getById('tableName', {id:1}); yield this.render('index', { title: rows[0].fieldName }); ...
代碼
請參考這個項目中的數據庫操作部分,項目處于持續開發中,數據庫示例部分取自該項目。
https://github.com/zhoutk/koadmin.git
小結
koa框架以co庫為核心組織,很好的用generator來解決了回調函數問題。