koa操作MongoDB数据库的封装

解决原生NodeJS 操作MongoDB 数据库的性能问题,封装成更小、更灵活的操作MongoDB库:

Config.js 将所要连接的数据的配置信息封装成一个模块:

const Config = {
    dbUrl:'mongodb://admin:123@localhost:27017/',
    dbName:'hello'
}
module.exports=Config;

封装数据库时,引入数据库配置模块就好,首先通过 promise 封装:

const MongoClient = require('mongodb').MongoClient;
const Config = require('./config.js');

class Db {
    constructor() {

    }
    //连接数据库
    connect() {  
       console.time('time_connect')
       return new Promise((resolve, reject) => {
           MongoClient.connect(Config.dbUrl, { useUnifiedTopology: true },(err, client) => {
               if (err) {
                   console.log("连接数据库失败")
                   reject(err)
                   return
               }
               const db = client.db(Config.dbName);
               console.log("连接数据库成功")
               console.timeEnd('time_connect')
               resolve(db)
           })
       })
    }
    //查询数据
    find(collectionName, json) {
        return new Promise((resolve, reject) => {
            this.connect().then((db) => { //连接数据库返回的db对象
                db.collection(collectionName).find(json).toArray(function (err, result) {
                    if (err) {
                        reject(err);
                        return;
                    }
                    resolve(result);
                })
            })
        })
    }
}

console.time('time_find1')
const myDb1 = new Db()
myDb1.find('user', {}).then(data=> { //find返回的是promise
    console.log("查询数据1");
    console.timeEnd('time_find1')
})

console.time('time_find2')
const myDb2 = new Db()
myDb2.find('user', {"name":"jack3"}).then(data => { //find返回的是promise
    console.log("查询数据2");
    console.timeEnd('time_find2')
})

/**
 * 连接数据库成功
 * time_connect: 49.488ms
 *  连接数据库成功
 * (node:12552) Warning: No such label 'time_connect' for console.timeEnd()
 * 查询数据1
 * time_find1: 65.757ms
 * 查询数据2
 * time_find2: 53.866ms
 */

发生了两个问题,一是每次查询都需要连接数据库(查询时连接数据库获得 client 对象 ),而是每次查询都需要重新创建一个实例(myDb1,myDb2 )

首先解决多次连接数据库的问题:

可以在构造函数中定义一个 dbClient 的属性,通过这个属性值判断是否成功连接了数据库:

测试:

const MongoClient = require('mongodb').MongoClient;
const Config = require('./config.js');

class Db {
    constructor() {
        this.dbClient="";
    }
    //连接数据库
    connect() {  
        console.time('time_connect')
        return new Promise((resolve, reject) => {
            if(!this.dbClient){
                MongoClient.connect(Config.dbUrl, { useUnifiedTopology: true }, (err, client) => {
                    if (err) {
                        console.log("连接数据库失败")
                        reject(err)
                        return
                    }
                    this.dbClient = client.db(Config.dbName);
                    console.log("连接数据库成功")
                    console.timeEnd('time_connect')
                    resolve(this.dbClient)
                })
            }
            else{
                resolve(this.dbClient)
            }
            
        })
    }
    //查询数据
    find(collectionName, json) {
        return new Promise((resolve, reject) => {
            this.connect().then(db => { //连接数据库连接返回的db对象
                db.collection(collectionName).find(json).toArray(function (err, result) {
                    if (err) {
                        reject(err);
                        return;
                    }
                    resolve(result);
                })
            })
        })
    }
}

const myDb = new Db()

console.time('time_find1')
myDb.find('user', {}).then(data => { 
    console.log("查询数据1");
    console.timeEnd('time_find1')
})
setTimeout(()=>{
    console.time('time_find2')
    myDb.find('user', {}).then(data => {
        console.log("查询数据2");
        console.timeEnd('time_find2')
    })
},3000)


/**
 * 连接数据库成功
 * time_connect: 47.374ms
 * 查询数据1
 *  time_find1: 70.183ms
 * 查询数据2
 * time_find2: 2.112ms
 */

但是多次实例化时,dbClient 重新为"",还是会再次去连接数据库:

const MongoClient = require('mongodb').MongoClient;
const Config = require('./config.js');

class Db {
    constructor() {
        this.dbClient="";
    }
    //连接数据库
    connect() {  
        console.time('time_connect')
        return new Promise((resolve, reject) => {
            if(!this.dbClient){
                MongoClient.connect(Config.dbUrl, { useUnifiedTopology: true }, (err, client) => {
                    if (err) {
                        console.log("连接数据库失败")
                        reject(err)
                        return
                    }
                    this.dbClient = client.db(Config.dbName);
                    console.log("连接数据库成功")
                    console.timeEnd('time_connect')
                    resolve(this.dbClient)
                })
            }
            else{
                resolve(this.dbClient)
            }
            
        })
    }
    //查询数据
    find(collectionName, json) {
        return new Promise((resolve, reject) => {
            this.connect().then(db => { //连接数据库连接返回的db对象
                db.collection(collectionName).find(json).toArray(function (err, result) {
                    if (err) {
                        reject(err);
                        return;
                    }
                    resolve(result);
                })
            })
        })
    }
}

const myDb1 = new Db()
console.time('time_find1')
myDb1.find('user', {}).then(data => { 
    console.log("查询数据1");
    console.timeEnd('time_find1')
})

const myDb2 = new Db()
setTimeout(()=>{
    console.time('time_find2')
    myDb2.find('user', { "name": "jack5" }).then(data => {
        console.log("查询数据2");
        console.timeEnd('time_find2')
    })
},2000)
/**
 * 连接数据库成功
 * time_connect: 42.574ms
 * 查询数据1
 * time_find1: 55.639ms
 * 连接数据库成功
 * time_connect: 5.675ms
 * 查询数据2
 * time_find2: 16.233ms
 */

通过单例模式解决这个问题:

const MongoClient = require('mongodb').MongoClient;
const Config = require('./config.js');

class Db {
    static getInstance(){
        if (!Db.instance) { //单例  解决多次实例化实例不共享的问题
            Db.instance=new Db()
        }
        return Db.instance
    }
    constructor() {
        this.dbClient="";
    }
    //连接数据库
    connect() {  
        console.time('time_connect')
        return new Promise((resolve, reject) => {
            if (!this.dbClient) { //解决数据库多次连接的问题
                MongoClient.connect(Config.dbUrl, { useUnifiedTopology: true }, (err, client) => {
                    if (err) {
                        console.log("连接数据库失败")
                        reject(err)
                        return
                    }
                    this.dbClient = client.db(Config.dbName);
                    console.log("连接数据库成功")
                    console.timeEnd('time_connect')
                    resolve(this.dbClient)
                })
            }
            else{
                resolve(this.dbClient)
            }
            
        })
    }
    //查询数据
    find(collectionName, json) {
        return new Promise((resolve, reject) => {
            this.connect().then(db => { //连接数据库连接返回的db对象
                db.collection(collectionName).find(json).toArray(function (err, result) {
                    if (err) {
                        reject(err);
                        return;
                    }
                    resolve(result);
                })
            })
        })
    }
}

const myDb1 = Db.getInstance() //相当于 myDb1 = new Db()
console.time('time_find1')
myDb1.find('user', {}).then(data => { 
    console.log("查询数据1");
    console.timeEnd('time_find1')
})

const myDb2 = Db.getInstance() //相当于 myDb2 = Db.instance
setTimeout(()=>{
    console.time('time_find2')
    myDb2.find('user', { "name": "jack5" }).then(data => {
        console.log("查询数据2");
        console.timeEnd('time_find2')
    })
},2000)

/**
 * 连接数据库成功
 * time_connect: 41.880ms
 * 查询数据1
 * time_find1: 55.679ms
 * 查询数据2
 *  time_find2: 2.045ms
 */

最后封装好的 db库将它暴露出去,这里还可以在初始化的时候就连接数据库,第一次操作数据库在初始化就连接上,而不是执行具体操作的时候连接数据库:

const MongoClient = require('mongodb').MongoClient;
const Config = require('./config.js');

class Db {
    static getInstance() {
        if (!Db.instance) { //单例  解决多次实例化实例不共享的问题
            Db.instance = new Db()
        }
        return Db.instance
    }
    constructor() {
        this.dbClient = "";
        this.connect()
    }
    //连接数据库
    connect() {
        //console.time('time_connect')
        return new Promise((resolve, reject) => {
            if (!this.dbClient) { //解决数据库多次连接的问题
                MongoClient.connect(Config.dbUrl, { useUnifiedTopology: true }, (err, client) => {
                    if (err) {
                        console.log("连接数据库失败")
                        reject(err)
                        return
                    }
                    this.dbClient = client.db(Config.dbName);
                    console.log("连接数据库成功")
                    //console.timeEnd('time_connect')
                    resolve(this.dbClient)
                })
            }
            else {
                resolve(this.dbClient)
            }

        })
    }
    //查询数据
    find(collectionName, json) {
        return new Promise((resolve, reject) => {
            this.connect().then(db => { //连接数据库连接返回的db对象
                db.collection(collectionName).find(json).toArray(function (err, result) {
                    if (err) {
                        reject(err);
                        return;
                    }
                    resolve(result);
                })
            })
        })
    }
    //修改数据(一条)
    update(collectionName, oldData, newData) {
        return new Promise((resolve, reject) => {
            this.connect().then((db) => {
                db.collection(collectionName).updateOne(oldData, { $set: newData }, (err, result) => {
                    if (err) {
                        console.log("修改数据失败")
                        reject(err)
                        return;
                    }
                    console.log("修改数据成功")
                    resolve(result)
                })
            })
        })
    }
    //添加数据(一条)
    insert(collectionName, addData) {
        return new Promise((resolve, reject) => {
            this.connect().then(db => {
                db.collection(collectionName).insertOne(addData, (err, result) => {
                    if (err) {
                        console.log("添加数据失败")
                        reject(err)
                        return
                    }
                    console.log("添加数据成功")
                    resolve(result)
                })
            })
        })
    }
    //删除数据(一条)
    remove(collectionName, deleteData) {
        return new Promise((resolve, reject) => {
            this.connect().then(db => {
                db.collection(collectionName).removeOne(deleteData, (err, result) => {
                    if (err) {
                        console.log("删除数据失败")
                        reject(err)
                        return
                    }
                    console.log("删除数据成功")
                    resolve(result)
                })
            })
        })
    }

}

module.exports = Db.getInstance()

 引入封装好的 db 库,通过 kob 操作:

const Koa = require('koa')
const app = new Koa()
const router = require('koa-router')()
const render = require('koa-art-template')
const views = require('koa-views')
const path = require('path')
const Db = require('./util/db')

//配置art-template
render(app, {
    root: path.join(__dirname, 'views'),   // 视图的位置
    extname: '.html',  // 后缀名
    debug: process.env.NODE_ENV !== 'production'  //是否开启调试模式
})

router.get('/', async (ctx)=>{
    let res = await Db.find('user', { "name": "李华" })
    //console.log(res) //{ _id: 5eef338f7701003ebcbbafa3, name: '李华', age: 18 }
    await ctx.render('index', {
        name: res[0].name,
        age: res[0].age
    }) 
})



app.use(router.routes());   
app.use(router.allowedMethods());
app.listen(3000);

原文地址:https://www.cnblogs.com/shanlu0000/p/13179112.html