npm install mysql
如果需要以前的版本0.9.x系列的文档,请访问v0.9 branch. 有时你可以从github中安装最新版本的node-mysql,具体怎么做请参考下面的示例:
npm install felixge/node-mysql
这是一个node.Js的mysql驱动程序。这个驱动完全是用javascript写的,不需要任何编译工作。完全的遵守MIT开源协议。 这里有一个简单的示例告诉你如何使用它:
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'me',
password : 'secret'
});
connection.connect(); //建立连接
connection.query('SELECT 1 + 1 AS solution', function(err, rows, fields) {
if (err) throw err;
console.log('The solution is: ', rows[0].solution);
});
connection.end(); //释放连接
从上面这个例子中,你应该要知道如下的两点:
end()
函数关闭连接时候,你必须要确保你的所有查询都已经发送给了mysql服务器。否者end()之后的查询是无效的。推荐的一种连接方式:
var mysql = require('mysql');var connection = mysql.createConnection({
host : 'example.org',
user : 'bob',
password : 'secret'
});
connection.connect(function(err) {
// 成功连接! (unless err is set)
});
然而,一个连接只能隐式的调用一个query函数。调用方式如下:
var mysql = require('mysql');
var connection = mysql.createConnection(...);
connection.query('SELECT 1', function(err, rows) {
// 成功连接! (unless err is set)
});
上面的两种错误处理方法都合适,这取决于你更喜欢那一种方式来做数据库错误信息处理。任何一种连接上的错误(握手与网络)都会被视为致命错误,具体更多的错误处理信息可以查看Error Handling章节。
当我们使用Node-MySQL建立一个数据库连接的时候你可以通过下面这些选项:
参数名 | 代表值 |
---|---|
host | 数据库的主机名(默认: localhost) |
port | 数据库服务器的端口(默认: 3306) |
localAddress | 使用TCP连接的源IP地址(可选) |
socketPath | 使用Unix域套接字连接的路径,当使用 host 与 port 的时候可以忽略。 |
user | 这个是MySQL身份验证的用户名。 |
password | MySQL用户的密码。 |
database | 连接的数据库名(可选). |
charset | 连接之后的字符编码(默认:' UTF8_GENERAL_CI',值得提醒的是,这个值必须都是大写字母). |
timezone | 这个是储存数据的是当前时间。(默认:local). |
connectTimeout | MySQL初始连接的超时时间。单位是毫秒(默认:无超时限制)。 |
stringifyObjects | 把值转换为 Stringify 对象。(默认:false)。 |
insecureAuth | 允许使用老(不安全)的身份认证方式去连接MySQL数据库.(默认: false). |
typeCast | 是否把结果值转换为原生的 javascript 类型(默认: true). |
queryFormat | 一个可以自己定义查询格式函数(具体见Custom format)。 |
supportBigNumbers | 当在数据库中处理一个大数(BIGINT和DECIMAL)数据类型的时候,你需要启用这个选项(默认: false). |
dateStrings | 强制为date类型(TIMESTAMP,DATETIME,DATE)转化为一个字符串返回而不是转换成 javascript的date类型对象。(默认: false) |
debug | 答应协议详细信息到标准输出(默认: false). |
trace | 产生栈跟踪当在库的入口点出现error时,当调用数次很多时性能会略微下降(默认: true). |
multipleStatements | 允许每个mysql语句有多条查询.使用它时要非常注意,因为它很容易引起sql注入攻击(默认:false). |
flags | 使用连接标示符号标示出超过默认的值的连接。它也可以加到默认的黑名单连接中。更多信息参考(Connection Flags)。 |
ssl | 使用ssl参数对象(格式参考crypto.createCredentials参数)或者是用一个包含ssl配置的字符串名。目前仅仅支持亚马逊的ca证书,参考:https://rds.amazonaws.com/doc/rds-ssl-ca-cert.pem |
bigNumberStrings | 这个选项需要bigNumberStrings与 supportBigNumbers同时启用,强制把数据库中大数(BIGINT和DECIMAL)数据类型的值转换为javascript字符对象串对象返回。(默认:false)。当启用supportBigNumbers选项,但 bigNumberStrings是未启用的状态。当无法用javascript数字对象(JavaScript Number objects)所表达的时候就会返回的是一个big number字符串对象(值的范围要在 [-253, +253]之间).否则将会返回一个Number类型的对象。如果supportBigNumbers是false那么这个选项会被自动忽略。 |
除了利用这些选项来做对象使用,你也可以用一个字符串来标示。例如:
var connection = mysql.createConnection('mysql://user:pass@host/db?debug=true&charset=BIG5_CHINESE_CI&timezone=-0700');
注意:查询出来的值第一会尝试转换为json格式,如果转换失败.那么就会转换成纯文本的字符串.
有两种关闭连接的方式,当查询完成后最优雅关闭连接方式是调用end()方法,例如:
connection.end(function(err) {
// 连接在这里会被终止
});
这样可以确保在查询队列完成之后发送一个 COM_QUIT 包到mysql服务器。如果在发送 COM_QUIT 出现致命的错误。在回调函数里面有一个err参数可以使用。但是这个连接无论如何也会被关闭掉。
另外一种替代end()方法的是调用destroy()方法,这个方法会立即终止底层socket连接.destroy()方法确保了没有任何事件或回调再连接触发。
connection.destroy();
destroy()方法不像end()方法, destroy()方法是没有任何回调参数的。
直接使用连接池
var mysql = require('mysql');
var pool = mysql.createPool({
host : 'example.org',
user : 'bob',
password : 'secret'
});
pool.query('SELECT 1 + 1 AS solution', function(err, rows, fields) {
if (err) throw err;
console.log('The solution is: ', rows[0].solution);
});
连接可以轻易的共享一个连接或者是管理多个连接:
var mysql = require('mysql');
var pool = mysql.createPool({
host : 'example.org',
user : 'bob',
password : 'secret'
});
pool.getConnection(function(err, connection) {
// 连接成功! (除非有err )
});
如果你想在连接前设置session变量并且获取使用。你可以监听connection事件:
pool.on('connection', function(connection) {
connection.query('SET SESSION auto_increment_increment=1')
});
当你的连接完成后,只需要调用connection.release()方法。将连接返回到池中,以供其他人再次使用。
var mysql = require('mysql');
var pool = mysql.createPool(...);
pool.getConnection(function(err, connection) {
// 使用连接
connection.query( 'SELECT something FROM sometable', function(err, rows) {
// 连接使用完成后释放连接
connection.release();
// 不要在这里使用connection进行查询,因为连接已经被归还到连接池了
});
});
如果你想关闭连接并且从连接池中移除,使用connection.destory()方法代替。当你下一次需要使用的时候,池会自动创建一个新的连接。 通过池创建连接是非常缓慢的。如果你配置你的连接池最大的连接数为100,当你只需要同时使用5个连接时候,它也仅仅只会创建5个连接。这种连接的循环方式是一种轮询(round-robin)的方式。采取的是从连接池顶部到底部的方式。
池连可以接受一些连接的参数选项。当一个连接创建之后,这些参数选项通过简单的构造传递到连接里面。池连接参数可以接受下面这些参数。waitForConnections:判断当前的连接是否可用并且是否已经达到了连接数的上限。如果是true。这个连接就会加入到连接队列中同时把状态转换为可用状态。如果是false,那么会立即返回一个错误给开发者。(默认:true).connectionLimit:一次连接最大的连接数(默认:10)。queueLimit:从getConnection获取连接数并且判断是否超出了queneLimit限制的排队等待的连接值,如果是就返回一个错误。如果设置为0,就是不限制连队列数(默认:0)。
PoolCluster提供了多台主机连接功能(group&retry&selector):
// 创建连接
var poolCluster = mysql.createPoolCluster();
poolCluster.add(config); // anonymous group
poolCluster.add('MASTER', masterConfig);
poolCluster.add('SLAVE1', slave1Config);
poolCluster.add('SLAVE2', slave2Config);
// 目标主机组 : ALL(anonymous, MASTER, SLAVE1-2), 连接方式 : round-robin(default)
poolCluster.getConnection(function (err, connection) {});
// 目标主机组 : MASTER, 连接方式: round-robin
poolCluster.getConnection('MASTER', function (err, connection) {});
// 目标主机组 : SLAVE1-2, 连接方式 : order
// 如果不能连接到SLAVE1就连接SLAVE2(把SLAVE1从集群节点中删除)
poolCluster.on('remove', function (nodeId) {
console.log('REMOVED NODE : ' + nodeId); // 节点ID= SLAVE1
});
poolCluster.getConnection('SLAVE*', 'ORDER', function (err, connection) {
});
// 命名方式: 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) {
});
// 释放连接
poolCluster.end();
参数名 | 代表值 |
---|---|
canRetry | 如果是true,当连接失败时Poolcluster会尝试重新连接(默认:true)。 |
removeNodeErrorCount | 如果连接失败,节点的errorCount将会增加.当errorCount大于removeNodeErrorCount时,会从poolCluster中移除一个节点。(默认:5). |
defaultSelector | 默认选择器(默认:RR). |
RR | 选择一个交替(轮循) |
RANDOM | 由随机函数选择一个节点. |
ORDER | 按照顺序无条件的选择第一个节点。 |
var clusterConfig = {
removeNodeErrorCount: 1, // 连接失败后立即从把该节点移除
defaultSelector: 'ORDER'
};
var poolCluster = mysql.createPoolCluster(clusterConfig);
MySQL提供了ChangeUser的命令,允许你更改当前用户并且切换用户时不需要关闭底层socket连接:
connection.changeUser({user : 'john'}, function(err) {
if (err) throw err;
});
这个功能提供了几个有用的参数选项:
参数名 | 代表值 |
---|---|
user | 新用户的名字。(默认为切换新用户前第一个用户) |
password | 新用户的密码(默认为切换新用户前第一个用户) |
charset | 新的字符编码(默认为切换新用户前第一个用户) |
database | 新的数据库名(默认为切换新用户前第一个用户) |
这个功能有时有一个副作用,它会把所有的连接状态都重置(变量,事务). 注意:此操作出现的错误会被该模块视为致命错误处理。
由于网络问题你有可能丢失与MySQL服务器的连接。也有可能被服务器踢出连接,还有可能是服务器重启或是崩溃等等,这些都是致命的错误都被归为error对象里面。并且模块提供了err.code = 'PROTOCOL_CONNECTION_LOST'信息,更多的错误处理信息请参考Error Handling 。 与服务器重连是建立一个新的连接,一旦现在的连接断开就不能让这个连接重新连接。它必须重新建立一条连接,连接到数据库服务器。 在连接池里面,当连接断开时会从连接池里面把连接移除,当下次需要连接的时候调用getConnection创建一个新的连接。
为了避免被SQL注入攻击,你需要把用户提交过来的数据进行转义之后再放到SQL查询语句里面。模块提供了connection.escape()和Pool.escape()两种方法:
var userId = 'some user provided value';
var sql = 'SELECT * FROM users WHERE id = ' + connection.escape(userId);
connection.query(sql, function(err, results) {
// ...
});
或者,你可以使用占位符'?'来代替,同样的也能对传入的值进行转义:
connection.query('SELECT * FROM users WHERE id = ?', [userId], function(err, results) {
// ...
});
这看起来像是使用了MySQL的预处理语句,其实同样的在MySQL内部也是使用了类似connection.escape()的方法。 不同的是类型转义的值不同。请看下面的解释:
如果你留意了,你会发现转义查询值可以巧妙的用下面这种方式:
var post = {id: 1, title: 'Hello MySQL'};
var query = connection.query('INSERT INTO posts SET ?', post, function(err, result) {
// 非常完美!
});
console.log(query.sql); // INSERT INTO posts SET id = 1, title = 'Hello MySQL'
如果你觉得有必要自己去转义查询值,那么你可以直接去调用转义函数:
var query = "SELECT * FROM posts WHERE title=" + mysql.escape("Hello MySQL");
console.log(query); // SELECT * FROM posts WHERE title='Hello MySQL'
转义查询标示符 如果你不信任用户提交过来的标示符((database / table / column name))。那么你需要使用mysql.escapeId(identifier)来转义:
var sorter = 'date';
var query = 'SELECT * FROM posts ORDER BY ' + mysql.escapeId(sorter);
console.log(query); // SELECT * FROM posts ORDER BY date
它还支持添加合格的转义符。不过得把转义的分成两部分:
var sorter = 'date';
var query = 'SELECT * FROM posts ORDER BY ' + mysql.escapeId('posts.' + sorter);
console.log(query); // SELECT * FROM posts ORDER BY posts.date
或者,你可以使用占位符'??',它会自动转义查询的值:
var userId = 1;
var columns = ['username', 'email'];
var query = connection.query('SELECT ?? FROM ?? WHERE id = ?', [columns, 'users', userId], function(err, results) {
// ...
});
console.log(query.sql); // SELECT username, email FROM users WHERE id = 1
请注意:最后的一个字符转义目前还在实验阶段,而且语法随时可能发生改变。
当你使用.escape(),.query(),.escapeId()时可以有效的防御SQL注入攻击.
你可以使用mysql.format()去执行预处理多嵌套查询语句,利用适当的转义处理对于标示符与值.下面有一个简单的例子:
var sql = "SELECT * FROM ?? WHERE ?? = ?";
var inserts = ['users', 'id', userId];
sql = mysql.format(sql, inserts);
这种方法可以确保发送到数据库进行数据查询之前就能够保证查询语句的安全,在发送到数据库查询之前执行预查询,这个功能非常的有用。 由于mysql.format是由sql的String.format暴露出来的。所以你可以你还可以选择传递stringifyObject 和timezone对象(不强制要求)。 并且允许您自定义把对象转换字符串的方法。以及特定地区的时间和时区(location specific/timezone aware Date).
如果您喜欢其他样式的转义格式。你可以在连接配置选项中自定义你的功能函数。如果你还想使用escape()或其他内置的函数。可以直接调用 这儿我们提供了一个示例:
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" });
如果你想获取插入一行值之后,获取自动递增主键的ID。你可以这样获取:
connection.query('INSERT INTO posts SET ?', {title: 'test'}, function(err, result) {
if (err) throw err;
console.log(result.insertId);
});
当处理大数字的时候(超过javascriptNumber类型的精度),你需要启用supportBigNumbers以便把ID作为字符串类型读取。否者会抛出错误. 而且当从数据库中查询的数字是一个big number类型的时候,你也需要把supportBigNumbers选项设置为true,否则查询出来的数据在传输过来的时候由于精度限制,自动删除超出部分。
MySQL的协议是顺序执行的。所以这就意味这你需要建立多个连接去实现并行查询,你应该需要一个池(Pool)来管理连接。一个简单的方法就是每一个连接都创建的一个HTTP请求连接.
有时,你可能去执行一个大的查询,并且要处理查询返回的每一行结果。那么你可以尝试这样做:
var query = connection.query('SELECT * FROM posts');
query.on('error', function(err) {
// 处理错误,当出现错误时,会立即发出一个end事件
}).on('fields', function(fields) {
// the field packets for the rows to follow
}).on('result', function(row) {
// 处理工程中涉及到I/O可以在这里先暂停连接
connection.pause();
processRow(row, function() {
connection.resume();
});
}).on('end', function() {
// 获取所有查询内容
});
在上面的例子中请注意几件事情:
query对象提供了一个非常方便的方法.stream(可选).它把查询事件封装成了可读(Readable)的Streams2对象,此流可以很容易地通过管道downstream,并提供自动化暂停/恢复,基于downstream堵塞和可选highWaterMark。该流的objectMode参数默认设为true 。
例如,把查询结果通过piping导入到另一个流中(最大缓存为5)的简单例子:
connection.query('SELECT * FROM posts')
.stream({highWaterMark: 5})
.pipe(...);
出于安全考虑,多语句查询默认是禁用的.(如果值没有经过转义,那么很有可能会导致SQL注入攻击)。如果你想使用此功能,你必须在连接中启用它:
var connection = mysql.createConnection({multipleStatements: true});
一旦启用,你就可以执行多条语句查询了。例如:
connection.query('SELECT 1; SELECT 2', function(err, results) {
if (err) throw err;
// 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)
});
如果你有一个查询语句出现了错误,那么抛出的错误会包含在err.index属性里面并且会告诉你是哪条语句出现了错误。同时当发生错误时对于剩余的查询语句MySQL也会停止执行.
注意:目前利用流来执行多条查询语句的接口还在试验阶段。所以我很期待能够得到您的反馈。
你可以在你的查询语句里面调用MySQL驱动中自带的任何存储过程,如果你使用存储过程生成的多个结果集,其实也就与您使用多语句查询生成得出的结果是一样的。
当我们使用JOIN函数执行查询的时候得到的结果里面有很多字段是重复的。默认情况下Node-MySQL会按照列读取顺序把一些冲突的列名进行合并。但是这样有可能会导致一些接收到的值变得不可用。 不过,您可以指定在表名中嵌套您需要的列,就像这样:
var options = {sql: '...', nestTables: true};
connection.query(options, function(err, results) {
/* 结果将会像下面这样的数组形式返回:
[{
table1: {
fieldA: '...',
fieldB: '...',
},
table2: {
fieldA: '...',
fieldB: '...',
},
}, ...]
*/
});
或者你可以使用字符串分隔符合并成你想要的结果:
var options = {sql: '...', nestTables: '_'};
connection.query(options, function(err, results) {
/*
results will be an array like this now:
[{
table1_fieldA: '...',
table1_fieldB: '...',
table2_fieldA: '...',
table2_fieldB: '...',
},...]
*/
});
在连接层中可以支持简单的事务处理:
connection.beginTransaction(function(err) {
if (err) { throw err; }
connection.query('INSERT INTO posts SET title=?', title, function(err, result) {
if (err) {
connection.rollback(function() {
throw err;
});
}
var log = 'Post ' + result.insertId + ' added';
connection.query('INSERT INTO log SET data=?', log, function(err, result) {
if (err) {
connection.rollback(function() {
throw err;
});
}
connection.commit(function(err) {
if (err) {
connection.rollback(function() {
throw err;
});
}
console.log('success!');
});
});
});
});
请注意,执行START TRANSACTION, COMMIT,和 ROLLBACK 这些简单命令的方法分别是transaction(),Commit()和rollback(),了解MySQL中一些会造成隐式提交语句很重要。
这个模块包含了错误处理机制,不过在编码的时候你还应该去自己检查自己的代码。看看是否会有一些意想不到的错误。 这个模块中所有的错误都是javascript error的对象实例,同时它还有两个属性:
致命的错误都可以在回调函数中捕获到。在下面这个例子中,所引起的错误是因为改连接试图连接到一个无效的端口上面。因此错误对象会被传递到回调函数中并且能够使用err.code或err.fatal知道错误的具体情况:
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(err) {
console.log(err.code); // 'ECONNREFUSED'
console.log(err.fatal); // true
});
一般情况下错误是只委托给它所属的回调函数,例如下面这个例子。只有第一个回调函数接收到了错误信息,而第二个同样是按照预期的方式执行的并不会捕获第一个查询的错误:
connection.query('USE name_of_db_that_does_not_exist', function(err, rows) {
console.log(err.code); // 'ER_BAD_DB_ERROR'
});
connection.query('SELECT 1', function(err, rows) {
console.log(err); // null
console.log(rows.length); // 1
});
最后值得提醒的是,有时候你可以把错误处理不放在与查询一起的回调函数里面,就如同上面的那个例子。你可以把错误处理放在连接对象上的错误事件里面。就像下面这样:
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中是一个特殊的对象,如果它没有被挂在一个事件上而是单独出现,那么就很有可能出现堆栈错误并且关闭NodeJS程序进程. 最后:这个模块处理错误的初衷都不是悄悄的处理掉错误,你应该把错误的处理放在回调函数里面。但是如果你不喜欢这么麻烦并且忽略掉错误。那么你可以这样做:
// I am Chuck Norris:
connection.on('error',function(){
//...
});
这个模块的异常处理很安全,也就是说在回调函数中抛出一个错误之后你可以使用’uncaughtException‘或域(domain)去捕获错误,然后继续的去使用做下面的事情。
为了您的使用方面,这个驱动会自动把MySQL中的一些类型转换为原生的javascript的类型。下面列举的是转换的类型: Number:
Date:
Buffer:
String:
我们不建议你把类型转换这个参数禁用,但是如果你想禁用也可以在连接的时候就去做(这种方法可能在以后的版本中删除/改变):
var connection = require('mysql').createConnection({typeCast:false});
或者在查询层做:
var options = {sql: '...', typeCast: false};
var query = connection.query(options, function(err, results) {
});
你也可以通过一个处理函数来转换自己想要的类型,通过已知的一些字段的信息,像数据库,表名和字段名,数据类型和长度。如果你只想自己定义一个类型转换函数。你可以在查询的回调函数中做。例如你把TINYINT(1)转换为布尔值:
connection.query({
sql: '...',
typeCast: function (field, next) {
if (field.type == 'TINY' && field.length == 1) {
return (field.string() == '1'); // 1 = true, 0 = false
}
return next();
}});
警告:你必须使用解析器中内建的三个field函数中的一个,在你自定义的类型转换回调函数中。他们只能调用一次:
field.string()
field.buffer()
field.geometry()
//或者使用别名:
parser.parseLengthCodedString()
parser.parseLengthCodedBuffer()
parser.parseGeometryValue()
如果你需要使用field函数,。你可以在RowDataPacket.prototype._typeCast中找到关于field函数的一些文档资料
如果,由于某些原因你需要修改默认的连接标记,那么你可能需要使用flags选项。通过在连接配置的选项列表中添加这个选项,那么你就可以修改默认的连接标记.如果你不想使用默认的flag你可以使用一个减号来取消掉。或者在连接的配置选项列表里面添加一个flag选项,而不写值,仅仅就是一个flag名字在哪里(不区分大小写)。 PS:不想像去掉flag选项一样在flag的前面加一个+号:
请注意:有些默认的flag目前还不支持(例如:SSL,Compression)。
例子: 下面的例子是使用黑名单FOUND_ROWS flag例子:
var connection = mysql.createConnection("mysql://localhost/test?flags=-FOUND_ROWS");
默认标记:
其他的可用标记:
如果在运行过程中出现了问题,你可以在连接的配置选项中添加一个debug参数来帮助你调试:
var connection = mysql.createConnection({debug: true});
它会将输入的信息与输出的信息标准输出的显示出来,你还可以通过把数据包的类型转换成一个数据数组对象来方便调试:
var connection = mysql.createConnection({debug: ['ComQueryPacket', 'RowDataPacket']});