如何使用Node.js和Tedious从单个连接/事务中进行多个数据库调用

时间:2014-12-01 16:07:31

标签: javascript sql-server node.js transactions tedious

我正在尝试将NodeJS与Tedious(http://pekim.github.io/tedious/)sql server插件一起使用来进行多个数据库调用。我的意图是: 1.打开连接 2.开始交易 3.进行多个数据库(存储过程)调用,这些调用不会返回任何数据。 4.提交事务(或在错误时回滚)。 5.关闭连接

这是一个示例.js文件,(不使用事务)用于NodeJS,我试图进行多个数据库调用,并且失败并显示错误“请求只能在LoggedIn状态下进行,而不是在SentClientRequest状态下进行“。我没有尝试解决这个问题。

有谁知道如何解决这个问题?

var Connection = require('tedious').Connection;
var Request = require('tedious').Request;

var config = {
    userName: 'login',
    password: 'password',
    server: '127.0.0.1',
    options: { rowCollectionOnDone: true }
};

var max = 1;
for (var i = 0; i < max; i++) {
    var connection = new Connection(config);

    function executeStatement() {
        request = new Request("select 42, 'hello world'", function (err, rowCount) {
            if (err) {
                console.log(err);
            } else {
                console.log(rowCount + ' rows');
            }
        });

        request.on('row', function (columns) {
            columns.forEach(function (column) {
                console.log(column.value);
            });
        });

        request.on('doneInProc', function (rowCount, more, rows) {
        });

        request.on('doneProc', function (rowCount, more, rows) {
            console.log('statement completed!')
            connection.execSql(request);
        });

        request.on('returnStatus', function (status) {
            console.log('statement completed!')
        });

        connection.execSql(request);
    }

    connection.on('connect', function (err) {
        // If no error, then good to go...
        executeStatement();
    });
}
console.log('Done!');

4 个答案:

答案 0 :(得分:2)

您尝试在未建立的连接上执行语句。在致电executeStatement之前,您错过了错误处理程序。

connection.on('connect', function (err) {
    if (err) {
        console.log(err); // replace with your code
        return;
    };

    // If no error, then good to go...
    executeStatement();
});

修改

如何在串行事务中执行多个语句:

var statements = ["select 1", "select 2", "select 3"];

var transaction = new sql.Transaction(connection);
transaction.begin(function(err) {
    // ... error checks

    async.mapSeries(statements, function(statement, next) {
        var request = new sql.Request(transaction);
        request.query(statement, next);
    }, function(err, results) {
        // ... error checks

        transaction.commit(function(err, recordset) {
            // ... error checks

            console.log("Transaction commited.");
        });
    });
});

答案 1 :(得分:0)

您可以使用繁琐的连接池https://github.com/pekim/tedious-connection-pool

答案 2 :(得分:0)

您应该使用繁琐的连接池来创建多个连接池。 对于节点js,可在以下位置使用npm模块:https://www.npmjs.com/package/tedious-connection-pool

对于for循环中的每个新值,您可以获取新连接并在connection.reset事件上使用doneInProc。 您一直在做的情况是正确执行for循环的第一次迭代(LoggedIn State),并且在没有关闭或释放连接的情况下继续使用相同的连接对象(SentClientRequest state)。 因此,当代码到达for循环的第二次迭代时,相同的对象处于最终状态。 希望它能解决你的问题

答案 3 :(得分:0)

正如@zevsuld和@mannutech所说,乏味的连接池将启用多个连接,并防止同时请求进入您的服务器时出错。

下面是一个通用示例,它允许您在一个连接池中编写多个查询,并公开它们以供您的api使用。我只是添加这个,以防其他人来尝试完成这种实现。

const ConnectionPool = require('tedious-connection-pool');
const path = require('path');
require('dotenv').config({
  path: path.join(__dirname, '../../.env')
})

let Request = require('tedious').Request;

let poolConfig = {
  min: 10,
  max: 50,
  log: true
}

let connectionConfig  = {
  userName: process.env.user,
  password: process.env.password,
  server: process.env.server
};

//create the pool
let pool = new ConnectionPool(poolConfig, connectionConfig);

pool.on('error', function(err) {
  console.error(err);
});

// At this point in the code, we have established a connection pool.  If you run node, you'll see it log out all then connections to your database. 
// Let's add some methods which your server might use in fulfilling requests to various endpoints.  

let query1 = (cb, res, query) => {
  // acquire a connection:
  pool.acquire(function(err, connection) {
    if (err) {
      console.error(err);
      return;
    } else {
      // form your query
      let sql_query = `SELECT column1, colum2 from TABLE WHERE column1 LIKE '${query.param}%%' ORDER BY column1 ASC`
      // use the connection as usual:
      request = new Request(sql_query, (err, rowCount) => {
        if (err) {
          console.log(err);
          return;
        } else {
          // console.log('rowCount:', rowCount);
        }
        //release the connection back to the pool when finished
        connection.release();
      });
      let records = [];
      request.on("row", function(columns) {
        let rowArray = [];
        columns.forEach(function(column) {
          rowArray.push(column.value);
        });
        records.push(rowArray);
      });
      request.on("doneInProc", function() {
        cb(records, res);
      });
      // lastly exectue the request on the open connection.
      connection.execSql(request);
    }
  });
};

let query2 = (cb, res, query) => {
  // acquire a connection:
  pool.acquire(function(err, connection) {
    if (err) {
      console.error(err);
      return;
    } else {
      // form your query
      let sql_query = `SELECT column3, colum4 from TABLE2 WHERE column3 LIKE '${query.param}%%' ORDER BY column3 ASC`;
      // use the connection as usual:
      request = new Request(sql_query, (err, rowCount) => {
        if (err) {
          console.log(err);
          return;
        } else {
          // console.log('rowCount:', rowCount);
        }
        //release the connection back to the pool when finished
        connection.release();
      });
      let records = [];
      request.on("row", function(columns) {
        let rowArray = [];
        columns.forEach(function(column) {
          rowArray.push(column.value);
        });
        records.push(rowArray);
      });
      request.on("doneInProc", function() {
        cb(records, res);
      });
      // lastly exectue the request on the open connection.
      connection.execSql(request);
    }
  });
};

// Let's expose these two functions to the rest of your API:
module.exports = {
  query1,
  query2
}