NodeJS MySQL 转储
Posted
技术标签:
【中文标题】NodeJS MySQL 转储【英文标题】:NodeJS MySQL Dump 【发布时间】:2014-04-02 07:13:12 【问题描述】:我尝试编写一个基本的 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】:编写的代码甚至没有为我保存文件。好像有几个问题。不确定这是实际代码还是复制粘贴中丢失了某些内容。但是,根据您所拥有的:
一个重要的问题是您永远不会在代码中使用 connection.connect() 连接到数据库。
连接后要运行的代码应该在 connection.connect() 回调中。例如
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 语句并填充备份属性。
这是对代码的最小重写,它将按照您的意图进行。需要注意的重要一点是“计数器”,它管理何时写入文件和关闭连接。如果是我的,我会做出其他改变,包括:
使用“自我”而不是“我” 使用数字 for 循环而不是 for (... in ...) 语法 我自己的回调符合(err, stuff)的节点约定 一个更实质性的变化是我将重写它以使用 Promise,因为这样做可以让您免于对深度嵌套回调固有的混乱感到痛苦。我个人喜欢 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););
);
更新:如果您好奇,这里有一个使用 Promise 的大量注释实现。请注意,没有 cmets 解释 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');
【讨论】:
完美!谢谢你。对 NodeJS 来说还是相当新的。看来我需要开始研究承诺了。 不客气。当我发现它们时我很高兴,因为一旦事情变得超过几个层次的回调,我发现代码变得有点难以遵循,我也喜欢错误传播的“免费赠品”。 感谢编辑!我要看看,看看我能从你的例子中学到什么。我开始深入研究 Promise,它与我以前见过的任何东西都不一样(我主要习惯于用 php 编程)。再次感谢! 希望评论的例子能帮助你理解,虽然我不是一个承诺专家。在 Youtube 上,您可以找到 Q 的主要作者之一 Dominic Denicola 的会议演讲,您可能会觉得这很有帮助。正如我所提到的,还有其他方法可以处理涉及节点异步性质的回调***。以上是关于NodeJS MySQL 转储的主要内容,如果未能解决你的问题,请参考以下文章