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

問題解決,撒花!(寫字太累了草草收尾就這樣吧)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容