安装 $ npm install mysql 有关之前的0.9.x版本的信息, 请访问 v0.9分支。 有时我还会要求你从Github安装最新版以检查bug是否已修复。在这种情况下,请输入: $ npm install mysqljs/mysql 引言 这是node.js的mysql驱动。它是用JavaScript编写的,不需要编译,完全遵循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(err, rows, fields) { if (err) throw err; console.log('The solution is: ', rows[0].solution);});connection.end(); 从这个例子中,你可以了解到以下几点:
贡献者 感谢那些给node-mysql模块贡献代码的人们, 参见GitHub 贡献者页面。 另外我还要感谢下面的两个人:
赞助商 下面这些公司给这个项目提供了一些经济支持,请允许我花更多的时间感谢它们(按贡献时间的先后排序):
如果你有兴趣赞助一天或更多的时间, 请 与我们联系。 社区生态 如果你想讨论这个模块的有关问题,亦或是其它的问题,请访问以下的任意一个:
推荐的建立链接的一个方法: 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);}); 然后,我们可以通过建立一个连接来进行查询: var mysql = require('mysql');var connection = mysql.createConnection(...);connection.query('SELECT 1', function(err, rows) { // connected! (unless `err` is set)}); 以上二种方法都是正确且合适的。至于如何取舍,就要看你怎么去处理所遇到的错误了。不管哪种类型的错误,那都是致命的,我们需要去看所提示的具体的错误信息。 连接参数 在建立新连接时,可以设置以下参数:
终止连接 终止连接的方法有两种。调用end()方法可以正常地终止一个连接: connection.end(function(err) { // 连接终止}); 这种方法将确保给MySQL服务器发送COM_QUIT包之前所有队列中的查询都会被执行。如果在发送COM_QUIT包之前发生了致命错误,那么会给回调函数传递一个err参数,但是不管怎样连接都会关闭。 另外一种终止连接的方法是调用destroy()方法。该方法会立即终止底层套接字(underlying socket)。另外,destroy()不会触发更多的事件和回调函数。 connection.destroy(); 和end()方法不同,destroy()方法不使用回调参数。 连接池连接 直接使用连接池。 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(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', database : 'my_db'});pool.getConnection(function(err, connection) { // connected! (unless `err` is set)}); 当连接完成后,调用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.destroy()方法。在下次需要时连接池会再创建一个新的连接。 连接池创建连接很慵懒。如果你配置的连接池最大支持100个连接,但同时只使用了5个连接,那么它只创建5个连接。连接还采用了循环轮转方式,即连接建立在连接池顶部,返回到连接池底部。 当从连接池中恢复之前的某个连接时,会给服务器发送一个ping包以检查连接是否正常。 连接池的配置参数 连接池接受所有与连接相同的配置参数。创建新连接时,配置参数只是简单的传给连接对象的构造器。除此配置外,连接池还支持一些额外的参数:
连接池的事件 连接 连接池里面创建了一个新连接时,会触发一个连接事件。如需要在使用此连接之前设置会话变量,将要对此事件进行监听。 pool.on('connection', function (connection) { connection.query('SET SESSION auto_increment_increment=1')}); 入列 队列中等待可用连接的回调函数被触发时,连接池将触发此事件。 pool.on('enqueue', function () { console.log('Waiting for available connection slot');}); 在连接池中关闭所有连接 如不再需要连接池时,你必须关闭所有连接。否则Node.js脚本的事件流一直处于活跃状态,最终会被MySQL服务器会关闭。脚本占用连接池,这是典型的情况!另外可以以优雅的方式的关闭服务器。关闭所有连接池中的连接,中使用end方法: pool.end(function (err) { // all connections in the pool have ended}); end方法接受一个可选的回调函数,以通知(一次)所有已关闭的连接。优雅关闭连接,队列中所有的查询都能被执行,但关闭连接池的时间会不一样。 一旦pool.end()被调用,pool.getConnection及其它操作将不再被执行! 集群连接池 集群连接池提供多主机连接.(分组&重试&选择器) // createvar poolCluster = mysql.createPoolCluster();// add configurations (the config is a pool config object)poolCluster.add(config); // add configuration with automatic namepoolCluster.add('MASTER', masterConfig); // add a named configurationpoolCluster.add('SLAVE1', slave1Config);poolCluster.add('SLAVE2', slave2Config);// remove configurationspoolCluster.remove('SLAVE2'); // By nodeIdpoolCluster.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-robinpoolCluster.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 });poolCluster.getConnection('SLAVE*', 'ORDER', 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) {});// close all connectionspoolCluster.end(function (err) { // all connections in the pool cluster have ended}); 集群连接池选项
var clusterConfig = { removeNodeErrorCount: 1, // Remove the node immediately when connection fails. defaultSelector: 'ORDER'};var poolCluster = mysql.createPoolCluster(clusterConfig); 更换用户并且改变连接状态 MySQL 提供了一个changeUser 命令,该命令允许你在不关闭下列socket的情况下,改变当前用户和连接的其余部分. connection.changeUser({user : 'john'}, function(err) { if (err) throw err;}); 该特性的可用选项有:
有时有用的是这个功能的副作用,即会重置任何连接的状态(变量,事物等). 在集群连接池模式下执行changeUser操作如果遇到错误将被视为致命错误. 服务断开连接 由于网络问题、MySQL服务器重启或崩溃,连接超时等问题,你有可能会断开与MySQL服务器的连接.所有上述事件被认定为致命错误,并且将返回错误码'PROTOCOL_CONNECTION_LOST'.查看更多信息,请参照错误处理章节. 下一个连接将以建立一个新的连接的方式来获取.当前设计下,一旦终止,现有的一个连接对象将不能够被重连. 连接池状态下,端开的链接将会从连接池中移除并释放空间,下一次调用getConnection时将用释放的空间创建新连接。 执行查询 最基本的执行查询的方法是在一个对象中调用 .query()函数 (比如一个连接或应用池实例)。 最简单的 .query()形式是 .query(sqlString, callback),第一个参数是一条SQL字符串,第二个参数是回调: 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(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),在查询时带有大量的高级可选项,比如 转义查询值(escaping query values),联结重叠列名(joins with overlapping column names),超时(timeouts), 和 类型转换(type casting)。 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)}); 注意一种第二和第三形式的结合体也可使用,其中占位符以参数来传递,并且不能做为选项对象。值参数会覆盖选项对象中的值。 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注入,每当需要在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(err, results) { // ...}); 另外,也可以使用 ? 作为查询字符串中的占位符,替代你想要转义的值。例如: connection.query('SELECT * FROM users WHERE id = ?', [userId], function(err, results) { // ...}); 使用多个占位符则传入的值会依次填入。例如,下方查询中foo将等于a、bar将等于b、baz将等于c,而id将会被赋值为userId的值: connection.query('UPDATE users SET foo = ?, bar = ?, baz = ? WHERE id = ?', ['a', 'b', 'c', userId], function(err, results) { // ...}); 这样看起来与MySQL中prepared statements相似,但它内部其实使用了之前的connection.escape() 方法。 注意 它与prepared statements的另一点不同之处是,即使是在注释和字符串中的 ? 也会被替换。 不同类型的值转义的方式是有区别的,其区别如下:
如果你足够细心,可能已经注意到了,这种占位符转义可以写出简洁的代码: var post = {id: 1, title: 'Hello MySQL'};var query = connection.query('INSERT INTO posts SET ?', post, function(err, result) { // Neat!});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' 查询标识符转义 如果用户提供了不可信的查询标识符(数据库名、表名、列名),你应该用 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(err, results) { // ...}); 同时它还支持限定符,并对限定符两边的内容都进行转义。 var sorter = 'date';var sql = 'SELECT * FROM posts ORDER BY ' + connection.escapeId('posts.' + sorter);connection.query(sql, function(err, results) { // ...}); 另外,你还可以用 ?? 作为占位符来替代你想要转义的标识符,如: 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()时,为了防止SQL注入,对象的键将被 .escapeId() 转义。 预查询 你可以使用 mysql.format 来创建一个多插入点的查询语句,对id和值可以使用适当的转义处理 。下面是一个简单的例子: var sql = 'SELECT * FROM ?? WHERE ?? = ?';var inserts = ['users', 'id', userId];sql = mysql.format(sql, inserts); 这样你就获得了一个有效并且安全的查询语句,然后可以把它发送给数据库。如果你希望在发送给数据库之前就准备好查询语句,这种方法很有用。因为mysql.format是从sqlString.format派生而来的,所以你还可以选择传递stringifyObject 和timezone对象(但不强制要求),并且允许您通过自定义的方式将对象转换成字符串,以及特定地区的时间和时区(location specific/timezone aware Date). 自定义格式如果你想使用其他样式的转义格式,你可以在连接配置选项中自定义一个自定义的格式函数。如果你还想使用内置的escape()函数或其它的连接函数,可以访问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' }); 获取插入行的id 如果你把一行插入到一个有自增主键的表中,可以这样获得插入的ID: connection.query('INSERT INTO posts SET ?', {title: 'test'}, function(err, result) { if (err) throw err; console.log(result.insertId);}); 当处理大数字的时候(超过javascript的Number类型的精度),你需要启用supportBigNumbers选项,以便把插入行的ID作为字符串类型读取出来,否者会抛出一个错误。 当从数据库中查询的数字是一个big number类型的时候,也需要把supportBigNumbers选项设置为true,否则查询出来的数据由于精度限制,会进行四舍五入。 取得受影响的行数 你可以从 insert,update 或者 delete 子句中取得受影响的行数。 connection.query('DELETE FROM posts WHERE title = 'wrong'', function (err, result) { if (err) throw err; console.log('deleted ' + result.affectedRows + ' rows');}) 取得被改变的行数 你可以从 update 子句取得被改变的行数。 '被改变的行数' 不同于 '受影响行数' ,它不更新行数,而是改变值。 connection.query('UPDATE posts SET ...', function (err, result) { if (err) throw err; console.log('changed ' + result.changedRows + ' rows');}) 获取连接ID 你可以取得MySQL的连接ID(“线程ID”),这是一个给定的连接,使用的是线程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) { // 处理错误,这之后会触发 'end' 事件 }) .on('fields', function(fields) { // 字段信息 }) .on('result', function(row) { // 暂停连接。如果你的处理过程涉及到 I/O 操作,这会很有用。 connection.pause(); processRow(row, function() { connection.resume(); }); }) .on('end', function() { // 所有数据行都已经接收完毕 }); 在上面的示例中,有几点需要注意:
此外,你可能需要知道,目前不可能在流式处理过程中处理单独的行列,它们总是完全缓存的。如果你在 MySQL 方面有流式处理大量字段的经验,希望你能在这里分享。 通过 Streams2 管道输出查询对象提供了一个便利的方法 .stream([options]) 将查询事件封装在 Readable Streams2 对象中。这个流对象能通过管道简单的流向下游,并基于下游阻塞程度和 highWaterMark 选项自动提供暂停/恢复功能。流对象的 objectMode 参数设置为 true且不可改变(如果你需要一个字节流,那你就得转换流,像示例中的 objstream 那样)。 示例,通过管道将查询结果输出到另一个流(最大缓冲 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) }); 如果查询中的某一条语句产生错误,则 Error 对象会有一个 err.index 属性用于指出是第几条语句导致的错误。一旦有错误发生,MySQL 会停止执行剩下的语句。 注意,流式处理多语句查询的接口是试验性的,我期待着对它的反馈。 |
|
来自: 昵称38670023 > 《待分类》