node-mssql несколько подготовленных операторов с одним соединением

Я бьюсь головой об эту библиотеку. Я попытался настроить одноэлементный промежуточный класс, который будет запускать соединение, а затем передавать его через статические методы. Моя проблема в том, что у меня возникают трудности с настройкой, чтобы соединение было уже открыто, когда пришло время выполнять запросы, но без необходимости повторного его открытия. Поскольку открытие соединения, конечно, асинхронно, я не могу просто поместить все в обратный вызов открытия, потому что это происходит совершенно в другом месте в другое время... единственное, что я могу сделать, это поделиться mssql.Connection, который "connecting": true, Вот почему я не делаю connection.connect() в Database.connect()

Как я могу открыть соединение и перейти к подготовке операторов и выполнению запросов, зная, что соединение открыто?

Моя проблема в том, что всякий раз, когда мой код достигает connection.connect() со второго раза это будет ошибка EALREADYCONNECTING потому что соединение уже открывается.

Я думал о том, чтобы выполнить какой-то пул Promise запросов, которые должны быть разрешены после того, как само соединение будет решено с помощью Promise, но сейчас мой мозг очень смущен!

let mssql = require('mssql');
let fs = require('fs');

class Database
{

  static connect(username, password, server, database)
  {
    if (Database.connection !== null) {
      return Database.connection;
    }

    let storedUsername = null;
    let storedPassword = null;
    let storedServer = null;
    let storedDatabase = null;

    try {
      fs.accessSync(__dirname + '/../../config.json');

      let data = fs.readFileSync(__dirname + '/../../config.json')
      data = JSON.parse(data);
      storedUsername = data.sql.username;
      storedPassword = data.sql.password;
      storedServer = data.sql.server;
      storedDatabase = data.sql.database;

    } catch (e) {
      // Do nothing
    }

    var config = {
      user: username || storedUsername || '',
      password: password || storedPassword || '',
      server: server || storedServer || 'localhost',
      database: database || storedDatabase || '',
    }

    Database.connection = new mssql.Connection(config);

    return Database.connection;
  }

  static getConnection()
  {
    if (Database.connection === null) {
      try {
        Database.connect();
      } catch (e) {
        throw new Error('Database.getConnection: Database not connected.');
      }
    }

    return Database.connection;
  }

  static getInstance()
  {
    return mssql;
  }

  static query(query, fields)
  {
    if (typeof query !== 'string' || typeof fields !== 'object') {
      throw new Error("Invalid parameters");
    }

    let db = Database.getInstance();
    let connection = Database.getConnection();
    let ps = new db.PreparedStatement(connection);
    let values = {};

    fields.forEach(function(current, index) {
      ps.input(current.name, current.type);
      values[current.name] = current.value;
    });

    connection.connect(function(err) {
      if (err) {
        throw err;
      }

      ps.prepare(query, function(err) {
        if (err) {
          throw new Error(err);
        }

        ps.execute(values, function(err, recordset, affected) {
          if (err) {
            ps.unprepare(function(err) {
              if (err) {
                throw new Error(err);
              }
            });
            throw new Error(err);
          }

          ps.unprepare(function(err) {
            if (err) {
              throw new Error(err);
            }
          });
        });
      });
    });
  }
}

Database.connection = null;

module.exports = Database;

2 ответа

Решение

На самом деле это не упоминается в документации модуля, но вы можете прослушивать события подключения. Поэтому, если вы хотите сохранить структуру и избежать повторений, вы можете прослушать Connection для connect событие. Это, кажется, идеальный ответ для меня.

let mssql = require('mssql');
let fs = require('fs');

class Database
{

  static connect(username, password, server, database)
  {
    if (Database.connection !== null) {
      return Database.connection;
    }

    let storedUsername = null;
    let storedPassword = null;
    let storedServer = null;
    let storedDatabase = null;

    try {
      fs.accessSync(__dirname + '/../../config.js');

      let config = require(__dirname + '/../../config')

      storedUsername = config.sql.username;
      storedPassword = config.sql.password;
      storedServer = config.sql.server;
      storedDatabase = config.sql.database;

    } catch (err) {
      console.log(err);
    }

    let configuration = {
      user: username || storedUsername || '',
      password: password || storedPassword || '',
      server: server || storedServer || 'localhost',
      database: database || storedDatabase || '',
    }

    Database.connection = new mssql.Connection(configuration);

    Database.connection.connect();
  }

  static disconnect()
  {
    Database.connection.close();
  }

  static getConnection()
  {
    if (Database.connection === null) {
      try {
        Database.connect();
      } catch (e) {
        throw new Error('Database.getConnection: Database not connected.');
      }
    }

    return Database.connection;
  }

  static getInstance()
  {
    return mssql;
  }

  static query(query, fields)
  {
    if (typeof query !== 'string' || typeof fields !== 'object') {
      throw new Error("Invalid parameters");
    }

    let db = Database.getInstance();
    let connection = Database.getConnection();
    let ps = new db.PreparedStatement(connection);
    let values = {};

    fields.forEach(function(current, index) {
      ps.input(current.name, current.type);
      values[current.name] = current.value;
    });

    connection.on('connect', function(err) {
      if (err) {
        throw err;
      }

      ps.prepare(query, function(err) {
        if (err) {
          throw new Error(err);
        }

        ps.execute(values, function(err, recordset, affected) {
          if (err) {
            ps.unprepare(function(err) {
              if (err) {
                throw new Error(err);
              }
            });
            throw new Error(err);
          }

          ps.unprepare(function(err) {
            if (err) {
              throw new Error(err);
            }
          });
        });
      });
    });
  }
}

Database.connection = null;

module.exports = Database;

Теперь, возможно, мне стоит пообещать эти обратные вызовы в query() метод.

Не совсем уверен, что шаблон, который вы используете, будет полезен с node.js, он будет полезен для программирования, не управляемого событиями, но чтобы заставить его работать с node.js, вы должны выполнить цикл, как предложено в комментариях. Это просто побеждает цель.

Второй момент заключается в том, что вы по сути создаете оболочку для класса mssql, которая добавляет еще один уровень сложности, возможно, вносит ошибки и значительно усложняет обслуживание. Следующий человек, работающий над этим кодом, будет знать mssql, но не будет знать класс, который вы создаете, и способы, которые вы должны были реализовать, чтобы заставить его работать.

Лучший способ использовать одно соединение - поместить все ваши запросы в ответ на обратный вызов.

try {
  fs.accessSync(__dirname + '/../../config.json');

  let data = fs.readFileSync(__dirname + '/../../config.json')
  data = JSON.parse(data);
  storedUsername = data.sql.username;
  storedPassword = data.sql.password;
  storedServer = data.sql.server;
  storedDatabase = data.sql.database;

} catch (e) {
  // Actually you must do something here. If nothing else
  // at least log it so that later on you are not left wondering 
  // why nothing seems to work.
}

var config = {
  user: username || storedUsername || '',
  password: password || storedPassword || '',
  server: server || storedServer || 'localhost',
  database: database || storedDatabase || '',
}

Database.connection = new mssql.Connection(config);
connection.connect(function(err) {
    // do everything here    
});
Другие вопросы по тегам