原文地址:https://www.npmjs.com/package/mysql
GitHub:https://github.com/mysqljs/mysql
中文文档:https://www.breword.com/mysqljs-mysql
这是一个Node.js模块,通过npm注册表获得。
在安装前需要下载并安装Node.js 0.6或更高版本。
安装完Node之后,使用npm install命令:
npm install mysql
有关之前0.9版本的信息。请访问v0.9分支。
有时也可能会要求你安装 Github上的最新版本来检查bug修复是否正常,若如此,请按如下方式安装:
npm install mysqljs/mysql
这是mysql数据库的node.js驱动程序,由javascript编写,无需编译,且是100% MIT许可的。
下面是使用范例:
var mysql = require('mysql');
var connection = mysql.createConnection({
host:'localhost',
user:'me',
password:'secret',
database:'my_db'
});
connection.connect();
connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) {
if (error) throw error;
console.log('The solution is: ', results[0].solution);
});
connection.end();
从上例,你可以了解到以下内容:
end()
完成的,它能确保在向mysql服务器发送退出指令之前执行所有剩余的查询。感谢为该模块贡献代码的人们。这是Github贡献者页面。
另外,我要感谢以下这些人::
Andrey Hristov (Oracle) - 帮助我解决协议(protocol)的一些问题.
Ulf Wendel (Oracle) - 帮助我解决协议(protocol)的一些问题.
balalal~~~
如果你想讨论这个模块,或提出问题,请使用以下方式之一:
推荐的连接数据库方式:
var mysql = require('mysql');
var connection = mysql.createConnection({
host:'example.org',
user:'bob',
password:'secret'
});
connection.connect(function(err) {
if (err) {
console.error('error connecting: ' + err.stack);
return;
}
console.log('connected as id ' + connection.threadId);
});
然而,也可以通过调用query()
来隐式的建立数据库连接:
var mysql = require('mysql');
var connection = mysql.createConnection(...);
connection.query('SELECT 1', function (error, results, fields) {
if (error) throw error;
// connected!
});
以你喜欢的方式处理错误。任何类型的连接错误(握手或网络)都被看成是致命的错误。有关详细信息,请参阅错误处理部分。
建立数据库连接时,你可以配置以下选项:
use database
)【可选】'local'
、 'Z'
、+HH:MM
或-HH:MM
形式的偏移量。比如'+08:00'
(默认值:‘local’)supportBigNumbers
和bigNumberStrings
会强制大数值列(BIGINT和DECIMAL类型列)始终作为JavaScript字符串对象返回。启用supportBigNumbers
但关闭bigNumberStrings
将只在无法用JavaScript Number对象精确表示时(当数值不在 [-2 53 ^{53} 53,+2 53 ^{53} 53] 范围时)返回大数值作为String对象,否则它们将作为Number对象返回。如果禁用了supportbignnumbers,则忽略此选项。(默认值:false)['TIMESTAMP']
(默认值:false)query()
使用多个mysql语句。启用时要小心,这可能会增加SQL注入攻击的危险。(默认值:false)除了将这些选项作为对象传递之外,还可以使用url字符串。例如:
ar connection = mysql.createConnection('mysql://user:pass@host/db?debug=true&charset=BIG5_CHINESE_CI&timezone=-0700');
注释:首先尝试将查询值解析为JSON,如果失败则假定为纯文本字符串。
ssl配置项接受字符串或对象。当给定字符串时,将使用以下预定义的配置文件:
当连接到其他服务器时,你需要提供一个选项对象,格式与tls.createSecureContext
相同。请注意,参数应是证书的字符串,而不是证书的文件名。下面是一个简单的例子:
var connection = mysql.createConnection({
host:'localhost',
ssl:{
ca : fs.readFileSync(__dirname + '/mysql-ca.crt')
}
});
你也可以不提供CA证书连接服务器,当然 ,不推荐这么做。
var connection = mysql.createConnection({
host:'localhost',
ssl:{
// DO NOT DO THIS
// set up your ca correctly to trust the connection
rejectUnauthorized: false
}
});
如果想更改默认的连接标志,则可以使用flags
选项,格式为字符串。若多个,以逗号分隔。在标志前加-
,表示将该标志从默认标志中移除。直接写标志名称或者在其前加+
,表示添加该到默认标志中(标志名称不区分大小写)。
var connection = mysql.createConnection({
// 移除 FOUND_ROWS 标志, 添加 IGNORE_SPACE 标志
flags:'-FOUND_ROWS,IGNORE_SPACE'
});
可用的表示有:
affectedRows
。Send the found rows instead of the affected rows as affectedRows.(默认开启)query()
前的空格。(默认开启)LOAD DATA LOCAL
(SQL语句)。该标志由连接配置项localInfile
控制。(默认开启);
分隔)。该标志由连接配置项multipleStatements
控制。(默认关闭)rejectUnauthorized
选项控制的,因此这个标志不起作用。(默认关闭)断开mysql服务器的连接有两种方式。end()
方法可以优雅的断开连接。
connection.end(function(err) {
// The connection is terminated now
});
它将确保在向MySQL服务器发送COM_QUIT数据包之前,所有先前排队的操作仍然存在。如果在发送COM_QUIT包之前发生致命错误,则会向回调提供err参数,但无论如何连接都会终止。
另一种断开连接的方式是调用destroy()
方法。这将立即终止底层的socket连接。另外,destroy()
不再触发连接(connection)事件,并且其没有回调函数。
connection.destroy();
不同于end()
,destroy()
方法没有回调函数作为参数。
无需逐个创建并管理连接,该模块提供了mysql.createPool(config)
来创建内置连接池。
创建一个连接池并使用:
var mysql = require('mysql');
var pool = mysql.createPool({
connectionLimit:10,
host:'example.org',
user:'bob',
password:'secret',
database:'my_db'
});
pool.query('SELECT 1 + 1 AS solution', function (error, results, fields) {
if (error) throw error;
console.log('The solution is: ', results[0].solution);
});
上述代码是pool.getConnection()
=> connection.query()
=> connection.release()
代码的快捷方式。使用pool.getConnection()
可以更方便为后续qurey操作共享连接状态。这是因为上述代码若多次调用pool.query()可能会创建多个并行运行的不同连接。下面是pool.getConnection()
使用的基本结构:
var mysql = require('mysql');
var pool = mysql.createPool(...);
pool.getConnection(function(err, connection) {
if (err) throw err; // not connected!
// Use the connection
connection.query('SELECT something FROM sometable', function (error, results, fields) {
// When done with the connection, release it.
connection.release();
// Handle error after the release.
if (error) throw error;
// Don't use the connection here, it has been returned to the pool.
});
});
如果你想关闭连接并将它从连接池中删除,请使用connection.destroy()
。下次需要时连接池将会创建一个新的连接。
连接池是惰性创建连接的。如果配置连接池允许最多100个连接,但每次只同时使用5个连接,则只会建立5个连接。连接是循环式的,其从池的顶部取出,并返回到池的底部。
当从池中检索到以前的连接时,将向服务器发送一个ping包,来检查连接是否依然良好。
连接池接受所有createConnection(options)
的配置项。在创建池连接时,只需将配置项传递给池连接的构造函数即可。除此之外,池连接还接受一些额外的配置项:
connectTimeout
略有不同,因为获取池连接并不总是涉及建立连接。如果连接请求排队,则该请求在队列中花费的时间不计入此超时。(默认10000)当从连接池中获取连接时,触发该事件。该事件被调用的时机为:该连接的所有获取连接活动被执行后,在该连接被递交给获取连接(pool.getConnection(callback)
)的回调函数之前。
pool.on('acquire', function (connection) {
console.log('Connection %d acquired', connection.threadId);
});
当连接池内建立新连接时,触发该事件。如果你需要设置会话变量,可以使用该事件。
pool.on('connection', function (connection) {
connection.query('SET SESSION auto_increment_increment=1')
});
当pool.getConnection(callback)
的回调函数在等待可用连接时,触发该事件。
pool.on('enqueue', function () {
console.log('Waiting for available connection slot');
});
当连接被释放回池时,触发该事件。该事件被调用的时机为:该连接的所有释放活动被执行之后。因此在该事件被调用时,其连接已经为空闲状态。
pool.on('release', function (connection) {
console.log('Connection %d released', connection.threadId);
});
当你使用完连接池,必须关闭所有连接,否则将使Node.js事件循环一直保持活跃状态,直到连接被NySQL服务器关闭。特别是在脚本中使用连接池,通常要执行此操作。要关闭池中所有连接,请使用pool.end()
方法:
pool.end(function (err) {
// all connections in the pool have ended
});
该方法接受一个可选的回调函数参数,你可以使用该回调函数来确定所有连接何时结束的。
一旦调用了pool.end()
,pool.getConnection()
和其他 与连接池有关的操作将不再被执行。 可以等到所有连接都被释放(release)在调用pool.end()
。如果你使用快捷的pool.query(sql,callback)
方式创建池连接(而不是pool.getConnection
→ connection.query
→ connection.release
),则在callback中调用pool.end()
。
pool. end
会在池的每个活跃连接中调用connection. end
。这将会发送一个QUIT数据包进行排队,并且设置一个标志(flag)来禁止pool. getConnection
创建新的连接。所有正在执行的命令或操作都能执行完,但新的命令不会再去执行。
池集群提供多主机连接。(分组 & 重试 & 选择器)(group & retry & selector)
// create
var poolCluster = mysql.createPoolCluster();
// add configurations (the config is a pool config object)
poolCluster.add(config); // add configuration with automatic name
poolCluster.add('MASTER', masterConfig); // add a named configuration
poolCluster.add('SLAVE1', slave1Config);
poolCluster.add('SLAVE2', slave2Config);
// remove configurations
poolCluster.remove('SLAVE2'); // By nodeId
poolCluster.remove('SLAVE*'); // By target group : SLAVE1-2
// Target Group : ALL(anonymous, MASTER, SLAVE1-2), Selector : round-robin(default)
poolCluster.getConnection(function (err, connection) {});
// Target Group : MASTER, Selector : round-robin
poolCluster.getConnection('MASTER', function (err, connection) {});
// Target Group : SLAVE1-2, Selector : order
// If can't connect to SLAVE1, return SLAVE2. (remove SLAVE1 in the cluster)
poolCluster.on('remove', function (nodeId) {
console.log('REMOVED NODE : ' + nodeId); // nodeId = SLAVE1
});
// A pattern can be passed with * as wildcard
poolCluster.getConnection('SLAVE*', 'ORDER', function (err, connection) {});
// The pattern can also be a regular expression
poolCluster.getConnection(/^SLAVE[12]$/, function (err, connection) {});
// of namespace : of(pattern, selector)
poolCluster.of('*').getConnection(function (err, connection) {});
var pool = poolCluster.of('SLAVE*', 'RANDOM');
pool.getConnection(function (err, connection) {});
pool.getConnection(function (err, connection) {});
pool.query(function (error, results, fields) {});
// close all connections
poolCluster.end(function (err) {
// all connections in the pool cluster have ended
});
errorCount
会自增。当errorCount
大于removeNodeErrorCount
时,移除PoolCluster中的一个节点。(默认5)var clusterConfig = {
removeNodeErrorCount: 1, // Remove the node immediately when connection fails.
defaultSelector: 'ORDER'
};
var poolCluster = mysql.createPoolCluster(clusterConfig);
MySQL提供了一个改变用户的命令,它允许你在不关闭底层socket的情况下,更改当前用户和连接的其他状态:
connection.changeUser({user : 'john'}, function(err) {
if (err) throw err;
});
接受的配置项:
这个功能的一个有用的副作用是,该函数还会重置一些连接状态(变量、事务等)。
在此操作期间遇到的错误将被此模块视为致命连接错误。
由于网络问题、服务器超时、服务器重新启动或崩溃,您可能会与MySQL服务器失去连接。所有这些事件都被认为是致命错误,并会有err被写入相应的会回调函数,code = 'PROTOCOL_CONNECTION_LOST'
。相关详情信息,请参阅错误处理部分。
重连是通过建立新连接来完成的。一旦终止,现有的connection对象会丢失。
断开的池(Pool)连接将从池中删除,从而为下次getConnection
调用时创建的新连接释放空间。
断开的集群(PoolCluster)连接将作为相关节点的错误计数,并增加该节点的错误代码。一旦给定节点上的错误超过removeNodeErrorCount
,它就会从集群中删除。发生这种情况时,如果不再有任何与模式匹配的节点,PoolCluster可能会发出POOL_NONEONLINE
错误。通过restoreNodeTimeout
配置可以设置给定超时后恢复脱机节点。
执行数据库查询最基础的方式就是在相应对象上调用其query()
方法(比如Connection对象,Pool对象或PoolNamespace对象)
query()
最基本的格式是query(sqlString, callback)
,其中sqlString是字符串类型的SQL语句,callback是回调函数:
connection.query('SELECT * FROM `books` WHERE `author` = "David"', function (error, results, fields) {
// error 如果在查询期间发生错误,error将是Error对象(如果有)
// results 将是查询结果(如果有)
// fields 将包含有关返回结果字段(如果有)的信息
});
query()
第二种格式为query(sqlString, values, callback)
,这种格式适用于使用占位符的情况下(详见转义查询值):
connection.query('SELECT * FROM `books` WHERE `author` = ?', ['David'], function (error, results, fields) {
// error will be an Error if one occurred during the query
// results will contain the results of the query
// fields will contain information about the returned results fields (if any)
});
query()
第三种格式为query(options, callback)
,你可以为query()配置高级选项,比如转义查询值、列名重合合并、超时时间和类型转换。
connection.query({
sql: 'SELECT * FROM `books` WHERE `author` = ?',
timeout: 40000, // 40s
values: ['David']
}, function (error, results, fields) {
// error will be an Error if one occurred during the query
// results will contain the results of the query
// fields will contain information about the returned results fields (if any)
});
注意,第二种和第三种格式可以混搭在一起:占位符的值是通过values
参数传递而非options
参数。
values
参数的值将会覆盖options.values
。
connection.query({
sql: 'SELECT * FROM `books` WHERE `author` = ?',
timeout: 40000, // 40s
},
['David'],
function (error, results, fields) {
// error will be an Error if one occurred during the query
// results will contain the results of the query
// fields will contain information about the returned results fields (if any)
}
);
注意:转义方法仅在SQL的NO_BACKSLASH_ESCAPES
模式被禁用时有效(MySQL服务器的默认状态)。
为避免SQL注入攻击,你应该始终在使用用户提供的数据前进行转义,可以使用mysql.escape()
,connection.escape()
或pool.escape()
方法进行转义:
var userId = 'some user provided value';
var sql = 'SELECT * FROM users WHERE id = ' + connection.escape(userId);
connection.query(sql, function (error, results, fields) {
if (error) throw error;
// ...
});
或者,你可以使用?
字符作为你想要转义的值的占位符,像这样:
connection.query('SELECT * FROM users WHERE id = ?', [userId], function (error, results, fields) {
if (error) throw error;
// ...
});
有多个占位符时,其与参数values数组同位置的值一一映射。比如,下面的query语句中,foo = a
,bar = b
, baz = c
,id = userId变量的值
:
connection.query('UPDATE users SET foo = ?, bar = ?, baz = ? WHERE id = ?', ['a', 'b', 'c', userId], function (error, results, fields) {
if (error) throw error;
// ...
});
这看起来像MYSQL中的预处理语句,其实,其内部已经调用connection.escape()
将values参数的值进行转义了。
注意:与预处理语句不同的是,?
就算出现在注释或字符串里,也会被替换成对应的值。
不同类型的值会按不同的规则进行转义,规则如下:
'YYYY-mm-dd HH:ii:ss'
字符串['a', 'b']
被转换成'a', 'b'
[['a', 'b'], ['c', 'd']]
被转换成('a', 'b'), ('c', 'd')
toSqlString()
方法的对象将调用其.toSqlString()
,并将返回值用作原始SQLkey = 'val'
对。如果属性的值是一个函数,将跳过;如果属性的值是一个对象,则调用其toString()
方法,并使用其返回的值NULL
使用转义,你可以让代码变得更精简:
var post = {id: 1, title: 'Hello MySQL'};
var query = connection.query('INSERT INTO posts SET ?', post, function (error, results, fields) {
if (error) throw error;
// Neat!
});
console.log(query.sql); // INSERT INTO posts SET `id` = 1, `title` = 'Hello MySQL'
toSqlString()
方法能够让你用函数进行复杂的查询:
var CURRENT_TIMESTAMP = { toSqlString: function() { return 'CURRENT_TIMESTAMP()'; } };
var sql = mysql.format('UPDATE posts SET modified = ? WHERE id = ?', [CURRENT_TIMESTAMP, 42]);
console.log(sql); // UPDATE posts SET modified = CURRENT_TIMESTAMP() WHERE id = 42
要构造带toSqlString()
方法的对象,你可以使用mysql.raw()
。创建的对象就算使用?
占位符也不会被更改。如果你将函数用作动态值传入,这一点很有用:
var CURRENT_TIMESTAMP = mysql.raw('CURRENT_TIMESTAMP()');
var sql = mysql.format('UPDATE posts SET modified = ? WHERE id = ?', [CURRENT_TIMESTAMP, 42]);
console.log(sql); // UPDATE posts SET modified = CURRENT_TIMESTAMP() WHERE id = 42
注意:传给mysql.raw()
的字符串会忽略所有转义函数,因此在传递未经验证的输入时要小心。
如果你想自己转义,也可以直接使用转义函数:
var query = "SELECT * FROM posts WHERE title=" + mysql.escape("Hello MySQL");
console.log(query); // SELECT * FROM posts WHERE title='Hello MySQL'
如果你不信任用户提供的SQL标识符(比如数据库名、表名、列名),可以使用mysql.escapeId(identifier)
,connection.escapeId(identifier)
或pool.escapeId(identifier)
进行转义,比如:
var sorter = 'date';
var sql = 'SELECT * FROM posts ORDER BY ' + connection.escapeId(sorter);
connection.query(sql, function (error, results, fields) {
if (error) throw error;
// ...
});
支持添加限定标识符。用户传来的标识符和添加的标识符都会被转义。
var sorter = 'date';
var sql = 'SELECT * FROM posts ORDER BY ' + connection.escapeId('posts.' + sorter);
// -> SELECT * FROM posts ORDER BY `posts`.`date`
如果不想把.
当成限定标识符,可以设置escapeId()
的第二个参数为true
,以将字符串当作文本标识符(literal identifier)来处理:
var sorter = 'date.2';
var sql = 'SELECT * FROM posts ORDER BY ' + connection.escapeId(sorter, true);
// -> SELECT * FROM posts ORDER BY `date.2`
或者,可以使用??
字符串作为占位符来表示你要转义的标识符,如:
var userId = 1;
var columns = ['username', 'email'];
var query = connection.query('SELECT ?? FROM ?? WHERE id = ?', [columns, 'users', userId], function (error, results, fields) {
if (error) throw error;
// ...
});
console.log(query.sql); // SELECT `username`, `email` FROM `users` WHERE id = 1
请注意,??
语法是实验性的,后续可能会有改动。
如果你向.escape()
或.query()
传入对象,可以使用.escapeId()
来避免SQL注入对象属性。
你可以运用标识符转义,并使用mysql.format
来生成一个具有多个插入点的query语句,例如:
var sql = "SELECT * FROM ?? WHERE ?? = ?";
var inserts = ['users', 'id', userId];
sql = mysql.format(sql, inserts);
由此,你将获得一个合法并已转义的query语句。然后你可以安全地将其发送到数据库。如果你想查看发送数据库前的query语句,mysql.format
会是很有用的。mysql.format
是通过SqlString.format实现的。您还可以选择(但不是必需的)传入stringifyObjectand
和timeZone
,这将允许你提供将对象转换为字符串的自定义方法,以及location-specific / timezone-aware的Date。
如果想修改query()
的解析模式,可以使用connection的配置项去编写自定义格式的方法。如果想使用内置的escape()
或任何其他connection函数,可以访问connection对象。
connection.config.queryFormat = function (query, values) {
if (!values) return query;
return query.replace(/\:(\w+)/g, function (txt, key) {
if (values.hasOwnProperty(key)) {
return this.escape(values[key]);
}
return txt;
}.bind(this));
};
connection.query("UPDATE posts SET title = :title", { title: "Hello MySQL" });
//UPDATE posts SET title = `Hello MySQL`
如果你向auto increment primary key
的表插入了一行数据,你可以这样获取插入行ID:
connection.query('INSERT INTO posts SET ?', {title: 'test'}, function (error, results, fields) {
if (error) throw error;
console.log(results.insertId);
});
当处理大数值(高于JavaScript数字精度限制)时,您应该考虑启用supportBigNumbers
配置项,以便能够将插入id作为字符串读取。否则将抛出错误。
当从数据库中获取大数值时也需要此配置项,否则,由于精度限制,您将获得舍入到数百或数千的值。
使用insert
,update
或delete
语句时,可以获取到受影响行的数量:
connection.query('DELETE FROM posts WHERE title = "wrong"', function (error, results, fields) {
if (error) throw error;
console.log('deleted ' + results.affectedRows + ' rows');
})
使用update
语句时,可以获取到修改行的数量。
changedRows
与affectedRows
的不同之处在于,值未更改的更新行不被列入changedRows
。
connection.query('UPDATE posts SET ...', function (error, results, fields) {
if (error) throw error;
console.log('changed ' + results.changedRows + ' rows');
})
你可以通过一个给定连接的threadId
属性获取到MYSQL的连接ID:
connection.connect(function(err) {
if (err) throw err;
console.log('connected as id ' + connection.threadId);
});
MySQL协议是顺序执行的,这意味着你需要多个连接才能并行执行查询。即你可以为接收的每个HTTP请求分别创建一个连接,并用池来管理连接。
有时你可能需要查询并处理大量的行,这样做:
var query = connection.query('SELECT * FROM posts');
query
.on('error', function(err) {
// Handle error, an 'end' event will be emitted after this as well
})
.on('fields', function(fields) {
// the field packets for the rows to follow
})
.on('result', function(row) {
// Pausing the connnection is useful if your processing involves I/O
connection.pause();
processRow(row, function() {
connection.resume();
});
})
.on('end', function() {
// all rows have been received
});
上例的几点说明:
pause()
使连接被节流前,接收一定数量的行。该数量取决于行的数量和大小。pause()
/ resume()
方法操作底层的socket和解析器。当调用pause()
后,result
事件不再被触发。query()
方法提供回调函数参数INSERT
/ UPDATE
成功,新旧两行的’result’事件都会被触发。Error: Connection lost: The server closed the connection.
。MySQL服务器上的netwritetimeout setting
选项可以让你配置该时间限制。另外,你可能想知道为什么当前无法流式处理单个行的所有列,当前它们会被整个地存入缓冲区。如果你确实有这方面使用场景,请告诉我(作者),同时也欢迎贡献代码。
query对象提供了stream([options])
方法,它将query事件包裹到了可读流( Readable Stream)对象。根据下游拥塞情况和配置项highWaterMark
,其可以很容易将数据引流下来,并实现了自动开 / 关(resume
/ pause
)。stream的objectMode
选项值被设置为true且无法更改(如果你需要字节流,则需要使用转换流,例如转换成对象流)。
例如,将查询结果传递到另一个流中变得很简单(该流的最大缓冲区为5个对象):
connection.query('SELECT * FROM posts')
.stream({highWaterMark: 5})
.pipe(...);
由于安全原因,多语句查询默认是禁止的(如果值没有正确转义,很可能遭受SQL注入攻击)。若要使用,可以在连接时开启这个特性:
var connection = mysql.createConnection({multipleStatements: true});
然后这样使用:
connection.query('SELECT 1; SELECT 2', function (error, results, fields) {
if (error) throw error;
// `results` is an array with one element for every statement in the query:
console.log(results[0]); // [{1: 1}]
console.log(results[1]); // [{2: 2}]
});
另外,也可以用流的方式获取多语句查询的结果:
var query = connection.query('SELECT 1; SELECT 2');
query
.on('fields', function(fields, index) {
// the fields for the result rows that follow
})
.on('result', function(row, index) {
// index refers to the statement this result belongs to (starts at 0)
});
如果其中一个语句发生错误,将会在Error对象中err.index
属性中得到错误信息,MYSQL也会停止执行剩下的语句。
请注意:流式多语句查询的接口是实验性的,我(作者)期待着对它的反馈。
可以像使用其他MYSQL驱动程序一样在查询中调用存储过程。如果存储过程产生多个结果集,它们将以与 多语句查询 结果相同的方式返回结果。
在执行表连接操作(join)时,可能会得到列名重叠的结果集。
默认情况下,其按照从MySQL接收到的列的顺序覆盖冲突列名,从而导致接收到的一些值不可用。
然而,你可以指定某列嵌套在表名下面,像这样:
var options = {sql: '...', nestTables: true};
connection.query(options, function (error, results, fields) {
if (error) throw error;
/* results will be an array like this now:
[{
table1: {
fieldA: '...',
fieldB: '...',
},
table2: {
fieldA: '...',
fieldB: '...',
},
}, ...]
*/
});
你也可以使用分隔字符来合并结果:
var options = {sql: '...', nestTables: '_'};
connection.query(options, function (error, results, fields) {
if (error) throw error;
/* results will be an array like this now:
[{
table1_fieldA: '...',
table1_fieldB: '...',
table2_fieldA: '...',
table2_fieldB: '...',
}, ...]
*/
});
我们在连接层(connection level)提供了简单的事务支持:
connection.beginTransaction(function(err) {
if (err) { throw err; }
connection.query('INSERT INTO posts SET title=?', title, function (error, results, fields) {
if (error) {
return connection.rollback(function() {
throw error;
});
}
var log = 'Post ' + results.insertId + ' added';
connection.query('INSERT INTO log SET data=?', log, function (error, results, fields) {
if (error) {
return connection.rollback(function() {
throw error;
});
}
connection.commit(function(err) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
console.log('success!');
});
});
});
});
请注意,beginTransaction()
、commit()
和rollback()
只是分别执行START TRANSACTION
、COMMIT
和ROLLBACK
命令的函数。重要的是要理解MySQL中的许多命令可以导致隐式提交,更多信息,可查阅MySQL文档。
使用connection.ping()
方法可将ping包发送出去。该方法向服务器发送一个ping包,当服务器响应时,将触发回调函数。如果发生错误,错误信息将被发送回调函数的error参数:
connection.ping(function (err) {
if (err) throw err;
console.log('Server responded to ping');
})
每个操作都有一个可选的超时配置项。这允许你为操作指定适当的超时时间。重要的是要注意,这些超时时间不是MySQL协议的一部分,而是客户端自己的超时机制。这意味着超时发生时,连接会被终止,进一步的操作无法执行。
connection.query({sql: 'SELECT COUNT(*) AS count FROM big_table', timeout: 60000}, function (error, results, fields) {
if (error && error.code === 'PROTOCOL_SEQUENCE_TIMEOUT') {
throw new Error('too long to count table rows!');
}
if (error) {
throw error;
}
console.log(results[0].count + ' rows');
});
该模块提供了一致的错误处理方法,为了编写可靠的应用程序,您应该仔细查看这些方法。
该模块创建的大部分错误都是Javascript的Error
对象的实例。此外,它们通常带有两个额外的属性:
String
。若是MySQL服务器错误,则使用MySQL服务器错误符号(如,'ER_ACCESS_DENIED_ERROR'
);若是Node.js错误,则使用Node.js的错误代码(如,'ECONNREFUSED'
);另外还有内部错误代码(如,‘PROTOCOL_CONNECTION_LOST’
)Number
。MySQL服务器错误编号。只有MySQL服务器错误时,其才有值Boolean
。表明此错误是否会终止连接。如果错误不是MySQL本身导致的,此属性不会被定义。String
。保存了发生错误的SQL语句。这对于使用更高级别的接口(例如,生成查询的ORM)很有用。String
。包含五个字符的SQLSTATE值。仅来自MySQL服务器错误。String
。包含描述错误的消息字符串。仅来自MySQL服务器错误。致命错误将传播到所有挂起的回调。在下面的示例中,尝试连接到无效端口会触发致命错误。因此,错误对象被传播到两个挂起的回调:
var connection = require('mysql').createConnection({
port: 84943, // WRONG PORT
});
connection.connect(function(err) {
console.log(err.code); // 'ECONNREFUSED'
console.log(err.fatal); // true
});
connection.query('SELECT 1', function (error, results, fields) {
console.log(error.code); // 'ECONNREFUSED'
console.log(error.fatal); // true
});
普通错误只被委托给其所属的回调。所以在下面的例子中,只有第一个回调收到错误,第二个查询如预期的那样工作:
connection.query('USE name_of_db_that_does_not_exist', function (error, results, fields) {
console.log(error.code); // 'ER_BAD_DB_ERROR'
});
connection.query('SELECT 1', function (error, results, fields) {
console.log(error); // null
console.log(results.length); // 1
});
如果发生致命错误并且没有挂起的回调,或者发生正常错误但没有属于其回调的,则该错误将作为连接对象上的’error’事件发出。如下:
connection.on('error', function(err) {
console.log(err.code); // 'ER_BAD_DB_ERROR'
});
connection.query('USE name_of_db_that_does_not_exist');
注意:'error’事件在 Node.js 中很特殊。如果它们没有对应的监听对象,错误发生地的堆栈信息会被打印出来,进程也将被杀死。
不要忽略错误。你应该为错误提供回调。如果你实在不想处理错误,可以参考下面的做法:
// I am Chuck Norris:
connection.on('error', function() {});
该插件会安全的处理异常。也就是说即使你某个回调函数抛出“uncaughtException"
这样的错误或域捕获的错误,程序依然会正常运行。
为了方便起见,默认情况下,这个驱动程序将MySQl类型转换为原生JavaScript类型。存在以下映射:
Number
Date
Buffer
String
注意:二进制字符集中的文本作为Buffer返回,而不是字符串返回。
不建议禁用类型转换(将来可能会取消/更改),但您目前可以在连接上这样做::
var connection = require('mysql').createConnection({typeCast: false});
或在query级别上:
var options = {sql: '...', typeCast: false};
var query = connection.query(options, function (error, results, fields) {
if (error) throw error;
// ...
});
也可以通过函数自定义类型转换,该函数会将一些列信息作为参数,比如数据库、表、列、列类型和列预定的大小。如果你仅想对某些类型进行转换,也可以通过该函数实现。
该函数提供两个参数:field
和next
。通过field对象触发解析器函数,并为特定字段返回值。
field
参数是一个对象,其包含了一些属性,如下:
String
,该列所属数据库名String
,该列所属的表名String
,该列的列名String
,该列的类型名(全大写)Number
,数据库指明的该列占用存储的大小next
参数是一个函数,当调用它时,将返回给定列的默认类型转换。
field
对象也包含了以下方法:
Buffer
MySQL协议是一个基于文本的协议。这就意味着,在网络传输上,所有列的类型都是以字符串形式呈现的,这就是为什么field
对象的方法都是字符串相关的。根据类型信息(如INT
),(列值)字符串会被强制转换为不同的Javascript类型(如数字)。
下面是一个将 TINYINT(1)
强制转换为 Boolean
的例子:
connection = mysql.createConnection({
typeCast: function (field, next) {
if (field.type === 'TINY' && field.length === 1) {
return (field.string() === '1'); // 1 = true, 0 = false
} else {
return next();
}
}
});
警告:在你自定义的类型转换回调函数里,要想触发解析器函数,你必须调用 field 的三个方法之一。而且,这三个方法都只能被调用一次。
如果你遇到了问题,为连接启用调试模式可能会有帮助到你:
var connection = mysql.createConnection({debug: true});
这将在标准输出(stdout)上打印所有传入和传出的数据包。你也可以通过传递一个类型数组来限制对数据包类型的调试:
//将调试限制在查询和数据包上
var connection = mysql.createConnection({debug: ['ComQueryPacket', 'RowDataPacket']});
安全问题不应该首先通过GitHub或其他公共论坛报告,而是保密,以便合作者评估报告并(a)设计修复并计划发布日期或(b)断言它不是安全问题(在这种情况下,它可以在公共论坛上发布,如GitHub问题)。
主要的私人论坛是电子邮件,通过电子邮件发送模块的作者或打开GitHub问题,简单地询问应该向谁解决安全问题,而不披露问题或问题类型。
理想的报告应该包括安全问题是什么以及如何利用安全问题的明确指示,理想情况下还应附带概念证明(PoC),以便协作者进行工作并验证潜在的修复。
该项目欢迎社区的贡献。你可以使用GitHub pull request。如果你不知道怎么创建GitHub pull request,请参阅GitHub文档:Creating a pull request。
pull request要求:
测试套件分为两个部分:单元测试和集成测试。单元测试可以在任何计算机上运行,而集成测试则需要设置MySQL服务器实例。
FILTER=unit npm test
设置环境变量MYSQL_DATABASE
,MYSQL_HOST
,MYSQL_PORT
,MYSQL_USER
和MYSQL_PASSWORD
。MYSQL_SOCKET
可以代替MYSQL_HOST
和MYSQL_PORT
,这样,直接与 UNIX socket连接。运行npm test。
例如,如果你安装了mysql, 并在localhost:3306上运行,且你没有为root用户设置密码,运行如下代码:
mysql -u root -e "CREATE DATABASE IF NOT EXISTS node_mysql_test"
MYSQL_HOST=localhost MYSQL_PORT=3306 MYSQL_DATABASE=node_mysql_test MYSQL_USER=root MYSQL_PASSWORD= FILTER=integration npm test