NodeJS MySQL转储

时间:2014-03-01 02:51:55

标签: mysql node.js

我试图编写一个基本的cron脚本来运行和“转储”一个mysql数据库。出于某种原因,当它“成功保存文件”时,它会创建文件,但它是空的。如果不是保存文件,而是执行console.log,它会打印一个空字符串。对我可能做错了什么的想法?

提前致谢。

var mysql_backup = function(){

    this.backup = '';
    this.mysql = require('mysql'),

    this.init = function(){
        this.connection = this.mysql.createConnection({
            user: 'root',
            password: 'root',
            database: 'test'
        });
    }

    this.query = function(sql, callback) {
        this.connection.query(sql, function (error, results, fields) {
            if (error) {
                throw error;
            }
            if (results.length  > 0) {
                callback(results);
            }
        });
    }

    this.get_tables = function(callback){
        var me = this;
        me.query('SHOW TABLES',
            function(tables) {
                for (var table in tables){
                    me.query(
                        'SHOW CREATE TABLE ' + tables[table].Tables_in_test,
                        function(r){
                            for (var t in r) {
                                me.backup += "DROP TABLE " + r[t].Table + "\n\n";
                                me.backup += r[t]["Create Table"] + "\n\n";
                            }
                        }
                    )
                }
                me.save_backup();
            });
    }

    this.save_backup = function(){
        var fs = require('fs');
        fs.writeFile("./backup_test.txt", this.backup, function(err) {
            if(err) {
                console.log(err);
            } else {
                console.log("The file was saved!");
            }
        });
    }

};

var db = new mysql_backup;
db.init();
db.get_tables();
db.connection.destroy();

1 个答案:

答案 0 :(得分:8)

编写的代码甚至没有为我保存文件。似乎有一些问题。不确定这是实际代码还是复制粘贴中丢失了一些东西。但是,根据你所拥有的:

一个重要的问题是,您永远不会使用connection.connect()连接到代码中的数据库。

连接后要运行的代码应该在connection.connect()回调中。 e.g。

connection.connect(function (err, empty) {
    if (err)
        throw new Error ('Panic');

    // if no error, we are off to the races...
}

但是,即使您快速重构代码以将最后一行包装在get连接回调中,您仍然会遇到问题,因为您在进行各种SQL调用之前破坏了连接,因此您需要将代码移动到某种最终回调中。

即使你这样做了,你仍然会有一个空文件,因为你从'SHOW TABLES'回调中调用save_backup而不是在你通过内部回调实际填充它之后获得CREATE TABLE语句并填充备份属性。

这是对代码的最小重写,它将按照您的意图执行。需要注意的一个重要事项是“计数器”,它管理何时写入文件并关闭连接。如果它是我的,我会做出其他改变,包括:

  • 使用'self'代替'me'
  • 使用数字for循环而不是for(... in ...)语法
  • 拥有自己的回调属于(错误,东西)
  • 的节点约定
  • 一个更实质性的变化是我会重写这个以使用promises,因为这样做会让你对深层嵌套回调所固有的混乱感到悲伤。我个人喜欢Q库,但这里有几个选项。

希望这会有所帮助。

var mysql_backup = function(){
    this.backup = '';
    this.mysql = require('mysql');

    this.init = function(){
        this.connection = this.mysql.createConnection({
            user     : 'root',
            password : 'root',
            database : 'test'
        });

    };

    this.query = function(sql, callback) {
        this.connection.query(sql, function (error, results, fields) {
            if (error) {
                throw error;
            }
            if (results.length  > 0) {
                callback(results);
            }
        });
    };

    this.get_tables = function(callback){
        var counter = 0;
        var me = this;
        this.query('SHOW TABLES',
            function(tables) {
                for (table in tables){
                    counter++;
                    me.query(
                        'SHOW CREATE TABLE ' + tables[table].Tables_in_mvc,
                        function(r){
                            for (t in r) {
                                me.backup += "DROP TABLE " + r[t].Table + "\n\n";
                                me.backup += r[t]["Create Table"] + "\n\n";
                            }
                            counter--;
                            if (counter === 0){
                                me.save_backup();
                                me.connection.destroy();

                            }
                        }
                    )
                }
            });
    };

    this.save_backup = function(){
        var fs = require('fs');
        fs.writeFile("./backup_test.txt", this.backup, function(err) {
            if(err) {
                console.log(err);
            } else {
                console.log("The file was saved!");
            }
        });
    }

};

var db = new mysql_backup;
db.init();
db.connection.connect(function (err){
    if (err) console.log(err);
    db.get_tables(function(x){;});

});

更新:如果你很好奇,这是一个使用promises的评论很强的实现。请注意,如果没有解释Q promise库函数的注释,它会比原始版本短一些,并且还提供更全面的错误处理。

var MysqlBackup = function(connectionInfo, filename){

    var Q = require('q');
    var self = this;
    this.backup = '';
    // my personal preference is to simply require() inline if I am only
    // going to use something a single time. I am certain some will find
    // this a terrible practice
    this.connection = require('mysql').createConnection(connectionInfo);

    function getTables(){
        //  return a promise from invoking the node-style 'query' method
        //  of self.connection with parameter 'SHOW TABLES'.
        return Q.ninvoke(self.connection,'query', 'SHOW TABLES');
    };

    function doTableEntries(theResults){

        // note that because promises only pass a single parameter around,
        // if the 'denodeify-ed' callback has more than two parameters (the
        // first being the err param), the parameters will be stuffed into
        // an array. In this case, the content of the 'fields' param of the
        // mysql callback is in theResults[1]

        var tables = theResults[0];
        // create an array of promises resulting from another Q.ninvoke()
        // query call, chained to .then(). Note that then() expects a function,
        // so recordEntry() in fact builds and returns a new one-off function
        // for actually recording the entry (see recordEntry() impl. below)

        var tableDefinitionGetters = [];
        for (var i = 0; i < tables.length ; i++){
            //  I noticed in your original code that your Tables_in_[] did not
            //  match your connection details ('mvc' vs 'test'), but the below
            //  should work and is a more generalized solution
            var tableName = tables[i]['Tables_in_'+connectionInfo.database];

            tableDefinitionGetters.push(Q.ninvoke(self.connection, 'query', 'SHOW CREATE TABLE ' + tableName)
                                        .then(recordEntry(tableName)) );
        }

        // now that you have an array of promises, you can use Q.allSettled
        // to return a promise which will be settled (resolved or rejected)
        // when all of the promises in the array are settled. Q.all is similar,
        // but its promise will be rejected (immediately) if any promise in the
        // array is rejected. I tend to use allSettled() in most cases.

        return Q.allSettled(tableDefinitionGetters);
    };

    function recordEntry (tableName){
        return function(createTableQryResult){
            self.backup += "DROP TABLE " + tableName + "\n\n";
            self.backup += createTableQryResult[0][0]["Create Table"] + "\n\n";
        };
    };

    function saveFile(){
        // Q.denodeify return a promise-enabled version of a node-style function
        // the below is probably excessively terse with its immediate invocation
        return (Q.denodeify(require('fs').writeFile))(filename, self.backup);
    }

    // with the above all done, now you can actually make the magic happen,
    // starting with the promise-return Q.ninvoke to connect to the DB
    // note that the successive .then()s will be executed iff (if and only
    // if) the preceding item resolves successfully, .catch() will get
    // executed in the event of any upstream error, and finally() will
    // get executed no matter what.

    Q.ninvoke(this.connection, 'connect')
    .then(getTables)
    .then(doTableEntries)
    .then(saveFile)
    .then( function() {console.log('Success'); } )
    .catch( function(err) {console.log('Something went awry', err); } )
    .finally( function() {self.connection.destroy(); } );
};

var myConnection = {
    host     : '127.0.0.1',
    user     : 'root',
    password : 'root',
    database : 'test'
};

// I have left this as constructor-based calling approach, but the
// constructor just does it all so I just ignore the return value

new MysqlBackup(myConnection,'./backup_test.txt');