NodeJs——使用tedious连接 sql server

项目地址:tedious

安装

npm i --save tedious

配置 config.js

基础的配置信息包括
server -- 数据库所在服务器,
userName -- 用户名,
password -- 密码,
options { database:' '} 数据库名等配置信息,详细的配置在 官方API 可以看到

let Connection= require('tedious').Connection
let Request = require('tedious').Request
let connectionCfg = {
    server: '127.0.0.1',  
    userName: 'yourname',
    password: 'yourpassword',
    options: { database: 'database' }  
  }
let connection = new Connection('connectionCfg')
connection.on('connect', function (err){
    if (!err) {
      executeStatement(querySql)
    }
})

function executeStatement ( querySql) { 
    let request = new Request(querySql , (err, rowCount)=>{
      if (err) {
        console.error(err)
        return; //创建 request 实例失败
      }
    })
    var result = [];  
    request.on('row', function(columns,idx) {
      var obj = {}  
      columns.forEach( function(column) {
        if(column.value !== null){
         var key = column.metadata.colName
         var val = column.value
          obj[key] = val
        }
      });
    result.push(obj)
    })

    request.on('done', function ( rowCount, more, rows) {
      return result
    })
}

代码写到这里感觉还是一片顺利,实际上已经有一两个小坑需要提醒一下了。

  1. requestrow 事件,回调函数中的参数 columns 受 config.options.useColumnNames 影响,默认为false,设置为true或者其他任何类型转换会判断为true的值时,返回的结果为object类型,这里的设置决定了遍历方法。详细的config配置说明在 这里
  2. requestdone事件,官方的说法是:创建Request实例时使用普通的sql查询时,查询完毕会触发该事件,请求类型为存储过程的话,触发 doneInProcdoneProc事件,实际在在使用过程中,即使将普通 sql 查询语句拼接成字符串传入 Request实例中也不会触发done事件,建议还是使用doneProc事件。语法如下:
    request.on('doneProc', function (rowCount, more, returnStatus, rows) { });
    判断callback中的more == false时,视为请求完成。

常见问题

报错
RequestError:requests can only be made in the LoggedIn state, not the SentClientRequest state
在我将配置项用作 module 引入时,经常碰到这个问题: 进入页面->触发数据库操作->返回上一页->再返回该页时,就得到一个 Internal Server Error的500页面。
翻 issue 时发现主要是因为一个问题 , 套用作者原话:issue地址

only one request can be performed on a connection at a time.

在同一时间同一个连接只能执行一个请求
具体到我的代码中,和提出 issue 这位用户很相似。同样是向外暴露了一个execute引用一个该用户核心代码和作者的解答

controller.execute = function(session, admin, sqlCommand) {
    var MSSqlConnection = require('./MSSqlConnection');

    MSSqlConnection.on('connected', function(connection){
            _performQuery(connection, sqlCommand.toString());
    });
    ...
}

作者的解释

Your MSSqlConnection variable is a singleton, because require caches modules. So when this code is called for the second page request, a second listener is added for the MSSqlConnection's connected event. When the connected event is emitted from MSSqlConnection, both listeners are called. This results in two calls to the Tedious Connection object's execSql function. The second call occurs before the first request has completed, and the state machine in Connection detects and reports the problem.

大概的意思就是,第二次进入页面时,向 connected事件加了一个监听器,connected事件触发时,两个监听器都被调用,第二个请求发起时,第一个请求还没有结束(还处于 sentClientRequest的state 中),所以就报错了。

the Request's callback signifies the completion of the request, and that another request may not be made until it is called.

创建 Request实例的意味着该请求完成,在这之前,其他请求不会被调用。
你很难去确定前面的请求是否已经是完成状态,是否可以执行下一个request,之前我不做模块化的时候,将所有请求方法和connect全部放到一个对象中,没有出现过这个问题,代码结构类似这样

let nodeSql = {
  connect: function () {
    len connection = new Connection(config)
    connection.on('connect' , funciton(){
      nodeSql.connection = connection
      ....
    })
    connection.on('end', function(){
    nodeSql.connection = null
    })
    connection.on('end', function(){
    nodeSql.connection = null
    })
  },
  execute: function (querySql) {
    if(this.connection) {
     return nodeSql.executeStatement(querySql)
    }else{
      return Promise.resolve(this.connect())
      .then(()=>nodeSql.executeStatement(querySql))
    }
   }
}

这样的代码当时没遇到问题,所以我有点懵逼,只能认定是 module的问题,为了代码结构清晰又不能放弃 module。好在作者还提供了连接池的使用方式,使用也非常简单。 tedious-connection-pool

问题解决,撒花!(写字太累了草草收尾就这样吧)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249

推荐阅读更多精彩内容