zoukankan      html  css  js  c++  java
  • Node.js中的ORM

    ORM2是一款基于Node.js实现的ORM框架,名字相当的霸气,算是同类框架中非常出色的一款,具体介绍请猛击:https://github.com/dresende/node-orm2

    刚接触Node.js + MySQL,在引入项目之初,受Asp.Net经验的影响,产生了许多不小的麻烦。下面是我定义的一个BaseProvider,作为所有DB Provider的父类,提供了一些公共的方法和属性。

    function BaseProvider() {
        this.table_name = {};
        this.properties = {};
        this.opts = {};
        this.getProviderModel = function (callback) {
            define(this.table_name, this.properties, this.opts, function (error, model) {
                callback(error, model);
            });
        };
    };
    
    var define = function (name, properties, opts, callback) {
        ORM.connect(pomelo.app.get("mysql"), function (error, db) {
            if (error) {
                logger.error(error);
                callback(error);
            } else {
                db.settings.set("connection.pool", true);
                var model = db.define(name, properties, opts);
                callback(null, model);
            }
        });
    };
    View Code

    getProviderModel 相当于获得一个Model,具有CRUD的功能,简化我们写SQL语句的繁琐过程,下面是一个具体子类的实现。

    function Dungeon() {
        this.table_name = "dungeon_userdungeons";
        this.properties = {
            dungeon_userdungeon_id: Number,
            user_id: Number,
            dungeon_id: String,
            latest_scene_id: String,
            status: Number,
            create_datetime_utc: Date
        };
        this.opts = {id: 'dungeon_userdungeon_id'};
    
        this.getUserCompleteDungeonIds = function (user_id, callback) {
            this.getProviderModel(function (error, model) {
                model.aggregate(["dungeon_id"], {user_id: user_id, status: Dungeon.Status.Complete}).max("dungeon_id").groupBy("dungeon_id").get(function (error, userdungeons) {
                    var dungeon_ids = [];
                    userdungeons.forEach(function (e) {
                        dungeon_ids.push(e.dungeon_id);
                    })
                    callback(error, dungeon_ids);
                });
            });
        };
    };
    Dungeon.prototype = new BaseProvider();
    View Code

    在阅读ORM2的源码后,我发现了一个严重的问题,该方法用于连接,返回一个名为db的ORM对象,这个对象的职责是维护Nodejs到具体DB的连接信息、各种配置、连接池、缓存、Model等,而非ADO.Net中单纯的Connection,下面是ORM. Connect的部分代码。

    var connect = function () {
        try {
            var Driver = require("./Drivers/DML/" + proto).Driver;
            var settings = new Settings.Container(exports.settings.get('*'));
            var debug = extractOption(opts, "debug");
            var pool = extractOption(opts, "pool");
            var driver = new Driver(opts, null, {
                debug: (debug !== null ? Boolean(debug) : settings.get("connection.debug")),
                pool: (pool !== null ? Boolean(pool) : settings.get("connection.pool")),
                settings: settings
            });
    
            db = new ORM(proto, driver, settings);
    
            driver.connect(function (err) {
                if (typeof cb === "function") {
                    if (err) {
                        return cb(err);
                    } else {
                        return cb(null, db);
                    }
                }
    
                db.emit("connect", err, !err ? db : null);
            });
        } catch (ex) {
            if (ex.code === "MODULE_NOT_FOUND" || ex.message.indexOf('find module')) {
                return ORM_Error(ErrorCodes.generateError(ErrorCodes.NO_SUPPORT, "CONNECTION_PROTOCOL_NOT_SUPPORTED"), cb);
            }
            return ORM_Error(ex, cb);
        }
        return db;
    };
    View Code

    而我常用define方法,就是在上面的那个db中的models数组中push一个对象而已。

    ORM.prototype.define = function (name, properties, opts) {
        var i;
    
        properties = properties || {};
        opts = opts || {};
    
        for (i = 0; i < this.plugins.length; i++) {
            if (typeof this.plugins[i].beforeDefine === "function") {
                this.plugins[i].beforeDefine(name, properties, opts);
            }
        }
    
        this.models[name] = new Model({
            db: this,
            settings: this.settings,
            driver_name: this.driver_name,
            driver: this.driver,
            table: opts.table || opts.collection || ((this.settings.get("model.namePrefix") || "") + name),
            properties: properties,
            extension: opts.extension || false,
            indexes: opts.indexes || [],
            cache: opts.hasOwnProperty("cache") ? opts.cache : this.settings.get("instance.cache"),
            id: opts.id || this.settings.get("properties.primary_key"),
            autoSave: opts.hasOwnProperty("autoSave") ? opts.autoSave : this.settings.get("instance.autoSave"),
            autoFetch: opts.hasOwnProperty("autoFetch") ? opts.autoFetch : this.settings.get("instance.autoFetch"),
            autoFetchLimit: opts.autoFetchLimit || this.settings.get("instance.autoFetchLimit"),
            cascadeRemove: opts.hasOwnProperty("cascadeRemove") ? opts.cascadeRemove : this.settings.get("instance.cascadeRemove"),
            hooks: opts.hooks || {},
            methods: opts.methods || {},
            validations: opts.validations || {}
        });
    
        for (i = 0; i < this.plugins.length; i++) {
            if (typeof this.plugins[i].define === "function") {
                this.plugins[i].define(this.models[name], this);
            }
        }
    
        return this.models[name];
    };
    View Code

    ORM2在connect时就把mysql的连接池自己维护起来了,存在一个变量中,所以Provider的每一次操作都会ORM.connect,返回的db作为一个局部变量用完就丢弃,维护的连接池也没有保存,造成连接数太多,从而产生many connection error,下图是mysql统计的数据,丢失的连接数达1000多。

    clip_image001

    每次调用都是新的连接池。

    image

    我在系统启动时创建ORM的DB,存到BaseProvider的原型中。

    ORM.connect(pomelo.app.get("mysql"), function (error, db) {
        BaseProvider.prototype.db = db;
        console.log("init connect~");
    });

    Define的时候直接访问缓存的db变量。

    var define = function (name, properties, opts, callback) {
        var model = BaseProvider.prototype.db.define(name, properties, opts);
        callback(null, model);
    };

    我并发执行6个操作,连接池会创建6个连接满足执行需求。

    clip_image001[5]

    因为先前已经创建了6个连接,第二次执行的时候,则直接返回。

    clip_image002[4]

    测试了一下午,连接池终于正常了。

    clip_image003

  • 相关阅读:
    sql server 跨库操作
    FPGA使用技巧
    MATLAB学习(3)
    FPGA统计摄像头输出-基于MD9T112
    zedboard VmodCAM 图像采集 HDMI输出显示
    VmodCAM图像采集 VGA显示
    EDK中如何使用ISE中生成的IP
    如何将HDL文件实例化到XPS中
    ubuntu下安装 Source insight
    VmodCAM 初始化
  • 原文地址:https://www.cnblogs.com/fengxiang/p/3422327.html
Copyright © 2011-2022 走看看