使用es6特性封装async-mysql

node.js的mysql模块本身没有提供返回promise的函数,即是说都是用的回调函数,那么对于我们使用async函数是很不方便的一件事。node.js有一个mysql封装库叫mysql-promise,这个库提供使用函数拼凑sql语句,但我比较想用原生的sql语句,原因在于mysql对于query函数封装得比较完美,能对应select,delete,update,insert返回不同的结果,比如update和delete返回affectRows,select返回查询结果这样,再加上使用参数化的sql语句能防止sql注入,所以封装了一下mysql的npm包。

 

首先是package.json,对于Promise,建议使用bluebird.Promise,然后自然是mysql

 1 {
 2     "name": "async-mysql",
 3     "version": "1.0.0",
 4     "main": "index.js",
 5     "author": {
 6         "name": "kazetotori/fxqn",
 7         "email": "kakkouto98045@live.com"
 8     },
 9     "files": [
10         "lib",
11         "index.js"
12     ],
13     "dependencies": {
14         "mysql": "2.12.0",
15         "bluebird": "3.4.6"
16     }
17 }

 

这个库入口文件为index.js,这里仅仅作为一个导出文件使用,没有任何代码

1 module.exports.Connection = require('./lib/Connection').Connection;
2 module.exports.Pool = require('./lib/Pool').Pool;

 

首先来实现一下单个连接的各个函数,使用es6的class关键字和Symbol封装,比传统的构造函数更加直观

  1 const mysql = require('mysql');
  2 const bluebird = require('bluebird');
  3 const Promise = bluebird.Promise;
  4 var $originConn = Symbol('originConn');
  5 var $isPoolConn = Symbol('isPoolConn');
  6 var $isAlive = Symbol('isAlive');
  7 
  8 /**
  9  * This function is the factory of the standard promise callback.
 10  * @param {Function} resolve
 11  * @param {Function} reject
 12  * @return {Function} The standard promise callback.
 13  */
 14 function promiseFn(resolve, reject) {
 15     return (err, rst) => {
 16         if (err) reject(err);
 17         else resolve(rst);
 18     }
 19 }
 20 
 21 
 22 
 23 /**
 24  * Connection is the class that contains functions that each returns promise.
 25  * These functions are just converted from a Mysql.Connection Object.
 26  */
 27 class Connection {
 28 
 29     /**
 30      * Constructor, initialize the connection object.
 31      * @param {Object} config The configuration of the connection.
 32      * @param {Boolean} isPoolConn Orders the connection is in a pool or not.
 33      */
 34     constructor(config, isPoolConn = false) {
 35         if (config.query)
 36             this[$originConn] = config;
 37         else
 38             this[$originConn] = mysql.createConnection(config);
 39         this[$isPoolConn] = isPoolConn;
 40         this[$isAlive] = true;
 41     }
 42 
 43     /**
 44      * Connection config
 45      */
 46     get config() { return this[$originConn].config; }
 47 
 48     /**
 49      * Orders the connection is in a pool or not.
 50      */
 51     get isPoolConnection() { return this[$isPoolConn]; }
 52 
 53     /**
 54      * Orders the connection is destroyed or not.
 55      */
 56     get isAlive() { return this[$isAlive]; }
 57 
 58     /**
 59      * Orders the threadId of the connection.
 60      */
 61     get threadId() { return this[$originConn].threadId; }
 62 
 63     /**
 64      * Add listener of this connection.
 65      */
 66     get on() { return this[$originConn].on; };
 67 
 68     /**
 69      * Ternimate the connection immediately whether there's any query in quene or not.
 70      */
 71     destroy() {
 72         return new Promise((resolve, reject) => {
 73             this[$originConn].destroy();
 74             this[$isAlive] = false;
 75             resolve();
 76         });
 77     }
 78 
 79     /**
 80      * Ternimate the connection. This function will ternimate the connection after any query being complete.
 81      */
 82     end() {
 83         return new Promise((resolve, reject) => {
 84             this[$originConn].end(promiseFn(resolve, reject))
 85         })
 86             .then(() => {
 87                 this[$isAlive] = false;
 88             })
 89     }
 90 
 91     /**
 92      * Execute sql command with parameters.
 93      * @param {String} cmd The sql command would be executed.
 94      * @param {Array} params Parameters.
 95      * @return {Promise<any>} The sql result.
 96      */
 97     query(cmd, params) {
 98         return new Promise((resolve, reject) => {
 99             let conn = this[$originConn];
100             let args = [cmd];
101             let callback = promiseFn(resolve, reject);
102             if (params)
103                 args.push(params);
104             args.push(callback);
105             conn.query(...args);
106         });
107     }
108 
109     /**
110      * Begin transaction of the connection. Following queries would not be useful until the function commit or rollback called.
111      * @return {Promise<undefined>}
112      */
113     beginTran() {
114         return new Promise((resolve, reject) => {
115             let conn = this[$originConn];
116             conn.beginTransaction(promiseFn(resolve, reject));
117         });
118     }
119 
120     /**
121      * Commit a transaction.
122      * @return {Promise<undefined>}
123      */
124     commit() {
125         return new Promise((resolve, reject) => {
126             let conn = this[$originConn];
127             conn.commit((err) => {
128                 if (err) this.rollback().then(() => reject(err));
129                 else resolve();
130             })
131         });
132     }
133 
134     /**
135      * Rollback a transaction
136      * @return {Promise<undefined>}
137      */
138     rollback() {
139         return new Promise((resolve, reject) => {
140             let conn = this[$originConn];
141             conn.rollback(() => resolve());
142         });
143     }
144 }
145 
146 
147 /**
148  * PoolConnection is the class extending from Connection.
149  * Any object of this class is the connection in a connection pool.
150  */
151 class PoolConnection extends Connection {
152     constructor(originConn) {
153         super(originConn, true);
154     }
155 
156     /**
157      * Release the connection and put it back to the pool.
158      * @return {Promise<undefined>}
159      */
160     release() {
161         return new Promise((resolve, reject) => {
162             this[$originConn].release();
163             resolve();
164         });
165     }
166 }
167 
168 
169 
170 module.exports.Connection = Connection;
171 module.exports.PoolConnection = PoolConnection;

 

然后是连接池的部分

const Promise = require('bluebird').Promise;
const mysql = require('mysql');
const PoolConnection = require('./Connection').PoolConnection;
var $originPool = Symbol('originPool');
var $isAlive = Symbol('isAlive');


/**
 * Pool is the class that contains functions each returns promise.
 * These functions are just converted from the Mysql.Pool object.
 */
class Pool {

    /**
     * Constructor, initialize the pool.
     * @param {Object} config The pool config.
     */
    constructor(config) {
        this[$originPool] = mysql.createPool(config);
        this[$isAlive] = true;
    }

    /**
     * Orders the pool config.
     */
    get config() { return this[$originPool].config; }

    /**
     * Orders the pool is destroyed or not.
     */
    get isAlive() { return this[$isAlive]; }

    /**
     * Add listener to the pool.
     */
    get on() { return this[$originPool].on; }

    /**
     * Get a connection object from the pool.
     * @return {Promise<PoolConnection>}
     */
    getConn() {
        return new Promise((resolve, reject) => {
            this[$originPool].getConnection((err, originConn) => {
                if (err)
                    return reject(err);
                let conn = new PoolConnection(originConn);
                resolve(conn);
            });
        });
    }

    /**
     * Ternimate the pool. This function would ternimate the pool after any query being complete.
     */
    end() {
        return new Promise((resolve, reject) => {
            this[$originPool].end((err) => {
                if (err)
                    return reject(err);
                this[$isAlive] = false;
                resolve();
            })
        });
    }

    /**
     * Use a connection to query a sql command with parameters.
     * @param {String} cmd The sql command would be executed.
     * @param {Array} params Parameters.
     * @return {Promise<any>} 
     */
    query(cmd, params) {
        return new Promise((resolve, reject) => {
            let args = [cmd];
            let callback = (err, rst) => {
                if (err) reject(err);
                else resolve(rst);
            }
            if (params)
                args.push(params);
            args.push(callback);
            this[$originPool].query(...args);
        });
    }
}



module.exports.Pool = Pool;

 

 

最后加一个config,便于智能提示

var $host = Symbol('host');
var $port = Symbol('port');
var $localAddr = Symbol('localAddr');
var $socketPath = Symbol('socketPath');
var $user = Symbol('user');
var $pwd = Symbol('pwd');
var $db = Symbol('db');
var $charset = Symbol('charset');
var $timezone = Symbol('timezone');
var $connTimeout = Symbol('connTimeout');
var $stringifyObjs = Symbol('stringifyObjs');
var $typeCast = Symbol('typeCast');
var $queryFormat = Symbol('queryFormat');
var $supportBigNumbers = Symbol('supportBigNumbers');
var $bigNumberStrings = Symbol('bigNumberStrings');
var $dateStrings = Symbol('dateStrings');
var $debug = Symbol('debug');
var $trace = Symbol('trace');
var $multiStmts = Symbol('multipleStatements');
var $flags = Symbol('flags');
var $ssl = Symbol('ssl');

class MysqlConfig {
    constructor(config) {
        for (let k in config)
            this[k] = config[k];
    }

    get host() { return this[$host] }
    set host(val) { this[$host] = val }

    get port() { return this[$port] }
    set port(val) { this[$port] = val }

    get localAddress() { return this[$localAddr] }
    set localAddress(val) { this[$localAddr] = val }

    get socketPath() { return this[$socketPath] }
    set socketPath(val) { this[$socketPath] = val }

    get user() { return this[$user] }
    set user(val) { this[$user] = val }

    get password() { return this[$pwd] }
    set password(val) { this[$pwd] = val }

    get database() { return this[$db] }
    set database(val) { this[$db] = val }

    get charset() { return this[$charset] }
    set charset(val) { this[$charset] = val }

    get timezone() { return this[$timezone] }
    set timezone(val) { this[$timezone] = val }

    get connectTimeout() { return this[$connTimeout] }
    set connectTimeout(val) { this[$connTimeout] = val }

    get stringifyObjects() { return this[$stringifyObjs] }
    set stringifyObjects(val) { this[$stringifyObjs] = val }

    get typeCast() { return this[$typeCast] }
    set typeCast() { this[$typeCast] = val }

    get queryFormat() { return this[$queryFormat] }
    set queryFormat(val) { this[$queryFormat] = val }

    get supportBigNumbers() { return this[$supportBigNumbers] }
    set supportBigNumbers(val) { this[$supportBigNumbers] = val }

    get bigNumberStrings() { return this[$bigNumberStrings] }
    set bigNumberStrings(val) { this[$bigNumberStrings] = val }

    get dateStrings() { return this[$dateStrings] }
    set dateStrings(val) { this[$dateStrings] = val }

    get debug() { return this[$debug] }
    set debug(val) { this[$debug] = val }

    get trace() { return this[$trace] }
    set trace(val) { this[$trace] = val }

    get multipleStatements() { return this[$multiStmts] }
    set multipleStatements(val) { this[$multiStmts] = val }

    get flags() { return this[$flags] }
    set flags(val) { this[$flags] = val }

    get ssl() { return this[$ssl] }
    set ssl(val) { this[$ssl] = val }
}

module.exports.MysqlConfig = MysqlConfig;

 

 

测试代码

//Use this test.js need node version is higher than 7.0.0 .
//And need the node arg "--harmony".

const config = {
    "host": "localhost",
    "port": 3306,
    "user": "root",
    "database": "testDB",
    "charset": "UTF8_GENERAL_CI",
    "timezone": "local",
    "connectTimeout": 10000,
    "connectionLimit": 10
};
const Pool = require('./lib/Pool').Pool;
const Connection = require('./lib/Connection').Connection;
var pool = new Pool(config);
var conn = new Connection(config);

async function poolTest() {

    //pool.query()
    let result = await pool.query('SELECT * FROM tbltest WHERE name=?', ['wr']);
    console.log(result);

    //pool.getConn();
    let poolConn = await pool.getConn();
    console.log(poolConn.isPoolConnection);
    result = await poolConn.query('SELECT * FROM tbltest WHERE name=?', ['zs']);
    console.log(result);

    await pool.end();
    console.log(pool.isAlive);
}

async function connTest() {
    let rst = await conn.query('SELECT * FROM tbltest WHERE name=?', ['ls']);
    console.log(rst);
    await conn.beginTran();
    let count = (await conn.query('SELECT COUNT(*) FROM tbltest WHERE name=?', ['??']))[0]['COUNT(*)'];
    console.log(count);
    await conn.query('INSERT INTO tbltest(name) VALUES(?)', ['zhangsan']);
    if (count > 0) {
        await conn.commit();
        console.log('commit');
    }
    else {
        await conn.rollback();
        console.log('rollback');
    }

    rst = await conn.query('SELECT * FROM tbltest');
    console.log(rst);
}

poolTest();
connTest();

 

posted @ 2017-01-02 21:22  风邪取鸟  阅读(2923)  评论(1编辑  收藏  举报