与node.js和mongo建立单例连接

时间:2014-07-03 07:17:26

标签: node.js mongodb

以前我用mongodb和php一起查询我使用单例的数据库。这样我只实例化一次连接,然后重复使用它:

class MDB{
    protected static $instance;
    public static function use(){
        if(!self::$instance) self::$instance = new MongoClient();
        $db = self::$instance->selectDB('DB_name');
        return $db;
    }
}

我可以创建类Cats,并使用类似的方法添加addCat和showCats方法:

MDB::use->{'cats'}->insert([...]);
MDB::use->{'cats'}->find([...]);

现在我开始在node.js中使用mongodb。 Mongodb tutorial向我展示了这样的事情:

var MongoClient = require('mongodb').MongoClient;
MongoClient.connect("mongodb://localhost:27017/exampleDb", function(err, db) {
  if(err) { return console.dir(err); }

  var collection = db.collection('test');
  var doc1 = {'hello':'doc1'};
  collection.insert(doc1);
});

这基本上告诉我,我必须将所有节点操作设置为connect内部的回调。阅读该人提供的类似question

  

当您的应用启动并重复使用时,您可以打开一次MongoClient.connect   db对象。它不是每个单独的连接池.connect   创建一个新的连接池。

但是我无法理解我应该如何使用它(例如我的猫类)?

5 个答案:

答案 0 :(得分:10)

这是一种方法。您可以将数据库连接详细信息放在一个小模块中,在应用程序启动时对其进行初始化,然后从需要数据库连接的任何其他模块中使用该模块。这是我一直在使用的代码,并且在一个相当简单的内部应用程序中为我工作。

文件: DataAccessAdapter.js

var Db = require('mongodb').Db;
var Server = require('mongodb').Server;
var dbPort = 27017;
var dbHost = 'localhost';
var dbName = 'CatDatabase';

var DataBase = function () {
};

module.exports = DataBase;

DataBase.GetDB = function () {
    if (typeof DataBase.db === 'undefined') {
        DataBase.InitDB();
    }
    return DataBase.db;
}

DataBase.InitDB = function () {
    DataBase.db = new Db(dbName, new Server(dbHost, dbPort, {}, {}), { safe: false, auto_reconnect: true });

    DataBase.db.open(function (e, d) {
        if (e) {
            console.log(e);
        } else {
            console.log('connected to database :: ' + dbName);
        }
    });
}

DataBase.Disconnect = function () {
    if (DataBase.db) {
        DataBase.db.close();
    }
}

DataBase.BsonIdFromString = function (id) {
    var mongo = require('mongodb');
    var BSON = mongo.BSONPure;
    return new BSON.ObjectID(id);
}

然后从server.js开始,当你的应用程序启动时:

// Startup database connection
require('./DataAccessAdapter').InitDB();

当您需要使用数据库时,例如在“ Cat.js ”文件中,您可以执行以下操作:

var dataAccessAdapter = require('./DataAccessAdapter');

var Cat = function () {
    if (!Cat.db) {
        console.log('Initializing my Cat database');
        Cat.db = dataAccessAdapter.GetDB();
    }
    if (!Cat.CatCollection) {
            console.log('Initializing cats collection');
        Cat.CatCollection = Cat.db.collection('Cats'); // Name of collection in mongo
    }
    return Cat;
}

module.exports = Cat;

Cat.Name = null;
Cat.HasFur = false;

Cat.Read = function (catId, callback) {
    var o_id = dataAccessAdapter.BsonIdFromString(catId);
    Cat.CatCollection.findOne({ '_id': o_id }, function (err, document) {
        if (!document) {
            var msg = "This cat is not in the database";
            console.warn(msg);
            callback(null, msg);
        }
        else {
            callback(document);
        }
    });
}

我希望这对于看到不同的方法至少有点帮助。我并不认为自己是专家,并对此提出了一些反馈意见,但到目前为止,这个解决方案对我来说效果很好。

答案 1 :(得分:7)

我赞成了Scampbell的解决方案,但他的解决方案应该加强imho。 目前它不是异步的,InitDBGetDB()都应该有回调属性。

因此,每当您将数据库更改为连接时,它都会失败,因为它会在有机会连接到数据库之前返回。 如果始终连接到同一数据库,则该错误不存在(因此返回Database.db始终成功)

这是我的解决方案的错误修正/增强:

Database.InitDB = function (callback) {

  if (_curDB === null || _curDB === undefined ||_curDB === '') {
    _curDB = _dbName;
  }

  Database.db = new Db(_curDB,
                   new Server(_dbHost, _dbPort, {}, {}),
                              { safe: false, auto_reconnect: true });

  Database.db.open(function (err, db) {
    if (err) {
        console.log(err);
    } else {
        console.log('connected to database :: ' + _curDB);
        if (callback !== undefined) {callback(db);}
    }
  });
};

其余的功能也是如此。另请注意if (callback部分,它允许在app.js / server.js的开头无需参数调用Database.InitDB(),无论您的主文件是什么。

((我应该把我的回复写成对Scampbell解决方案的评论,但我没有足够的声誉这样做。同样对他的解决方案赞不绝口,这是一个很好的起点) )

答案 2 :(得分:5)

以下是在singleton上使用async await的内容。 在我的 db.js

var MongoClient = require('mongodb').MongoClient;

var DbConnection = function () {

    var db = null;
    var instance = 0;

    async function DbConnect() {
        try {
            let url = 'mongodb://myurl.blablabla';
            let _db = await MongoClient.connect(url);

            return _db
        } catch (e) {
            return e;
        }
    }

   async function Get() {
        try {
            instance++;     // this is just to count how many times our singleton is called.
            console.log(`DbConnection called ${instance} times`);

            if (db != null) {
                console.log(`db connection is already alive`);
                return db;
            } else {
                console.log(`getting new db connection`);
                db = await DbConnect();
                return db; 
            }
        } catch (e) {
            return e;
        }
    }

    return {
        Get: Get
    }
}


module.exports = DbConnection();

在所有将使用相同连接的模块中

var DbConnection = require('./db');

async function insert(data) {
    try {
        let db = await DbConnection.Get();
        let result = await db.collection('mycollection').insert(data);

        return result;
    } catch (e) {
        return e;
    }
}

答案 3 :(得分:1)

我使用打字稿连接到 node.js 中的 mongodb 的单例类的完整工作示例。

import {Db, MongoClient, MongoError} from 'mongodb'

// Connexion credentials type 
type MongoDBCredential = {
    dbName: string;
    username: string,
    password: string;
    cluster: string;
}

// Singleton DBInstance Class 
export class DbInstance {
 private static _instance: DbInstance;
 private _database: Db;
 private _dbClient: MongoClient;

 private constructor() {};

 public static async getInstance(cred: Readonly<MongoDBCredential>): Promise<DbInstance> {
 return new Promise((resolve, reject) => {
  if(this._instance) {
   resolve(this._instance);
  }
  this._instance = new DbInstance();
  this._instance._dbClient = new MongoClient(`mongodb+srv://${cred.username}:${cred.password}@${cred.cluster}.mongodb.net/${cred.dbName}?retryWrites=true&w=majority&readPreference=secondary`, {
    useNewUrlParser: true,
    useUnifiedTopology: true,
  });
  this._instance._dbClient.connect((error: MongoError) => {
     if(error) {
      reject(error);
     }
     this._instance._database = this._instance._dbClient.db(cred.dbName);
     resolve(this._instance);
   });
  });
}
 get db(): Db {
  return DbInstance._instance._database;
 }
 get client(): MongoClient {
  return DbInstance._instance._dbClient;
 }
}

// To use it  
const cred : MongoDBCredential = {  dbName: '***', username: '***', password: '***', cluster: '***' };
DbInstance.getInstance(cred).then((dbi: DbInstance) => {
// do your crud operations with dbi.db
 dbi.db.collection('employee').findOne({'salary': '80K€ ?'}).then(account => {
  console.info(account);
  dbi.client.close().
 });
}).catch((error: MongoError )=> console.error(error));

答案 4 :(得分:0)

您可以使用ES6类来制作真正的Singleton。

以下是打字稿中的一个示例:

import { MongoClient } from "mongodb";

class MongoSingleton {
    private static mongoClient: MongoClient;

  static isInitialized(): boolean {
    return this.mongoClient !== undefined;
  }

  static getClient(): MongoClient {
    if (this.isInitialized()) return this.mongoClient;

    // Initialize the connection.
    this.mongoClient = new MongoClient(mongoUri, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    });
    return this.mongoClient;
}

您可以改进该类,以在内部连接并保持连接,或在其他类中连接和断开连接。