db-mysql由於node-waf: not found
已經不能使用,能夠使用mysql代替.javascript
本文主要是[node-mysql]: https://www.npmjs.com/package/node-mysql 的翻譯,也去除了一部分本身暫時沒有使用到的,如集羣.html
npm install mysql
純JavaScript編寫,使用的MIT協議.java
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
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); });
也能夠直接經過查詢創建鏈接mysql
var mysql = require('mysql'); var connection = mysql.createConnection(...); connection.query('SELECT 1', function(err, rows) { // connected! (unless `err` is set) });
host
: The hostname of the database you are connecting to. (Default:localhost
)port
: The port number to connect to. (Default: 3306
)localAddress
: The source IP address to use for TCP connection. (Optional)socketPath
: The path to a unix domain socket to connect to. When used host
port
are ignored.user
: The MySQL user to authenticate as.password
: The password of that MySQL user.database
: Name of the database to use for this connection (Optional).charset
: The charset for the connection. This is called "collation" in the SQL-levelutf8_general_ci
). If a SQL-level charset is specified (like utf8mb4
)'UTF8_GENERAL_CI'
)timezone
: The timezone used to store local dates. (Default: 'local'
)connectTimeout
: The milliseconds before a timeout occurs during the initial connection10000
)stringifyObjects
: Stringify objects instead of converting to values. See'false'
)insecureAuth
: Allow connecting to MySQL instances that ask for the oldfalse
)typeCast
: Determines if column values should be converted to nativetrue
)queryFormat
: A custom query format function. See Custom format.supportBigNumbers
: When dealing with big numbers (BIGINT and DECIMAL columns) in the database,false
).bigNumberStrings
: Enabling both supportBigNumbers
and bigNumberStrings
forces big numbersfalse
).supportBigNumbers
but leaving bigNumberStrings
disabled will return big numbers as StringsupportBigNumbers
is disabled.dateStrings
: Force date types (TIMESTAMP, DATETIME, DATE) to be returned as strings rather thenfalse
)debug
: Prints protocol details to stdout. (Default: false
)trace
: Generates stack traces on Error
to include call site of librarytrue
)multipleStatements
: Allow multiple mysql statements per query. Be carefulfalse
)flags
: List of connection flags to use other than the default ones. It isssl
: object with ssl parameters or a string containing name of ssl profile. See SSL options.下面這樣經過字符串方式也能夠:git
var connection = mysql.createConnection('mysql://user:pass@host/db?debug=true&charset=BIG5_CHINESE_CI&timezone=-0700');
有兩種方式關閉鏈接:end和destroygithub
使用end回調關閉會更優雅一些,他會確保已經在隊列中的查詢會發送一個COM_QUIT
給mysql.sql
connection.end(function(err) { // The connection is terminated now });
使用destroy會直接粗暴關閉鏈接,不會觸發connection的任何回調函數.shell
connection.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); });
經過connection.release()
釋放鏈接數據庫
var mysql = require('mysql'); var pool = mysql.createPool(...); pool.getConnection(function(err, connection) { // Use the connection connection.query( 'SELECT something FROM sometable', function(err, rows) { // And done with the connection. // 釋放鏈接 connection.release(); // Don't use the connection here, it has been returned to the pool. }); });
若是你想從鏈接池掛壁一個鏈接,使用connection.destroy()
.固然若是有須要鏈接池會新建一個代替.
鏈接池對於鏈接時懶加載的.好比你配置了100個鏈接,而如今只使用了5個,那隻會初始化5個.
鏈接池回收一個鏈接,後會往mysql服務器發送一個ping,確認鏈接是否有效.
鏈接池能夠直接使用鏈接的選項,而後在新建鏈接時,直接用這些配置新建鏈接.鏈接池添加了下面的選項:
acquireTimeout
: The milliseconds before a timeout occurs during the connectionconnectTimeout
, because acquiring10000
)waitForConnections
: Determines the pool's action when no connections aretrue
, the pool will queue thefalse
, thetrue
)connectionLimit
: The maximum number of connections to create at once.10
)queueLimit
: The maximum number of connection requests the pool will queuegetConnection
. If set to 0
, there is no0
)創建鏈接會觸發connection
.
pool.on('connection', function (connection) { connection.query('SET SESSION auto_increment_increment=1') });
當有回調排隊等待鏈接時,觸發enqueue
pool.on('enqueue', function () { console.log('Waiting for available connection slot'); });
以前提到關閉鏈接池中的鏈接後,當須要使用時鏈接池會自動新建,因此使用connection.end()
或connection.destroy()
時沒法關閉鏈接池的,須要使用pool.end()
:
pool.end(function (err) { // all connections in the pool have ended });
在Connection
或Pool
實例上使用.query()
是最簡單的查詢.
第一種方式是直接拼接好查詢用的sql.query(sqlString, 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(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) });
第三種方式是使用options..query(options, callback)
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) { // ... });
佔位符是按順序替換的.
connection.query('UPDATE users SET foo = ?, bar = ?, baz = ? WHERE id = ?', ['a', 'b', 'c', userId], function(err, results) { // ... });
不一樣類型的參數,轉義規則是不同的:
true
/ false
'YYYY-mm-dd HH:ii:ss'
stringsX'0fa5'
['a', 'b']
turns into 'a', 'b'
[['a', 'b'], ['c', 'd']]
turns into ('a', 'b'), ('c', 'd')
key = 'val'
pairs for each enumerable property on the object. If the property's value is a function, it is skipped; if theundefined
/ null
are converted to NULL
NaN
/ Infinity
are left as-is. MySQL does not support these, and trying to insert them as values will trigger MySQL errors until they implement轉義還提供對象方式傳參數
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'
若是你對用戶提供的關鍵詞沒把我 (database / table / column name) ,能夠使用 mysql.escapeId(identifier)
,
connection.escapeId(identifier)
or 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
Please note that this last character sequence is experimental and syntax might change
When you pass an Object to .escape()
or .query()
, .escapeId()
is used to avoid SQL injection in object keys.
You can use mysql.format to prepare a query with multiple insertion points, utilizing the proper escaping for ids and values. A simple example of this follows:
咱們能夠使用mysql.format
來準備一個插入語句,解決轉義問題.
var sql = "SELECT * FROM ?? WHERE ?? = ?"; var inserts = ['users', 'id', userId]; sql = mysql.format(sql, inserts);
這樣咱們就能夠獲得一個安全有效,轉義好的查詢語句.mysql.format
是SqlString.format
暴露的,因此能夠傳入stringifyObject和timezone來自定義對象如何轉爲字符串.
若是咱們想使用其餘方式來轉義查詢語句,能夠使用connection的配置.能夠使用內置的.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自增加方式插入數據,你能夠這樣獲取id:
connection.query('INSERT INTO posts SET ?', {title: 'test'}, function(err, result) { if (err) throw err; console.log(result.insertId); });
When dealing with big numbers (above JavaScript Number precision limit), you should consider enabling supportBigNumbers
option to be able to read the insert id as a string, otherwise it will throw an error.
This option is also required when fetching big numbers from the database, otherwise you will get values rounded to hundreds or thousands due to the precision limit.
咱們能夠獲取影響(新建,修改,刪除)涉及的行數
connection.query('DELETE FROM posts WHERE title = "wrong"', function (err, result) { if (err) throw err; console.log('deleted ' + result.affectedRows + ' rows'); })
咱們能夠獲取update語句修改涉及的行數/
"changedRows" 不一樣於 "affectedRows" 不統計符合條件但沒有改變值的記錄. in that it does not count updated rows whose values were not changed.
connection.query('UPDATE posts SET ...', function (err, result) { if (err) throw err; console.log('changed ' + result.changedRows + ' rows'); })
connection.connect(function(err) { if (err) throw err; console.log('connected as id ' + connection.threadId); });
mysql是順序執行的,因此咱們須要使用多個鏈接來並行查詢.最簡答的最法是每一個http請求分配一個鏈接.
若是須要查詢大量數據並處理每行,能夠這樣作:
Sometimes you may want to select large quantities of rows and process each of them as they are received. This can be done like this:
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 });
Please note a few things about the example above:
pause()
. This number will depend on thepause()
/ resume()
operate on the underlying socket and parser. You are'result'
events will fire after calling pause()
.query()
method when streaming rows.'result'
event will fire for both rows as well as OK packetsError: Connection lost: The server closed the connection.
Additionally you may be interested to know that it is currently not possible to
stream individual row columns, they will always be buffered up entirely. If you
have a good use case for streaming large fields to and from MySQL, I'd love to
get your thoughts and contributions on this.
The query object provides a convenience method .stream([options])
that wraps
query events into a Readable
Streams2 object. This
stream can easily be piped downstream and provides automatic pause/resume,
based on downstream congestion and the optional highWaterMark
. The
objectMode
parameter of the stream is set to true
and cannot be changed
(if you need a byte stream, you will need to use a transform stream, like
objstream for example).
For example, piping query results into another stream (with a max buffer of 5
objects) is simply:
connection.query('SELECT * FROM posts') .stream({highWaterMark: 5}) .pipe(...);
因爲sql注入的安全問題,多語句查詢默認禁用.須要手動啓用{multipleStatements: true}
.
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}] });
Additionally you can also stream the results of multiple statement queries:
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) });
If one of the statements in your query causes an error, the resulting Error
object contains a err.index
property which tells you which statement caused
it. MySQL will also stop executing any remaining statements when an error
occurs.
Please note that the interface for streaming multiple statement queries is
experimental and I am looking forward to feedback on it.
跟普通語句同樣使用存儲過程就好.若是存儲過程返回了多個集合的數據,會像多語句查詢那樣返回結果集.
執行join語句時,極可能會收到重複的列名.
By default, node-mysql will overwrite colliding column names in the
order the columns are received from MySQL, causing some of the received values
to be unavailable.
However, you can also specify that you want your columns to be nested below
the table name like this:
var options = {sql: '...', nestTables: true}; connection.query(options, function(err, results) { /* results will be an array like this now: [{ table1: { fieldA: '...', fieldB: '...', }, table2: { fieldA: '...', fieldB: '...', }, }, ...] */ });
Or use a string separator to have your results merged.
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中提供事務
connection.beginTransaction(function(err) { if (err) { throw err; } connection.query('INSERT INTO posts SET title=?', title, function(err, result) { if (err) { return connection.rollback(function() { throw err; }); } var log = 'Post ' + result.insertId + ' added'; connection.query('INSERT INTO log SET data=?', log, function(err, result) { if (err) { return connection.rollback(function() { throw err; }); } connection.commit(function(err) { if (err) { return connection.rollback(function() { throw err; }); } console.log('success!'); }); }); }); });
beginTransaction(), commit() 和 rollback()只是簡單執行START TRANSACTION, COMMIT, 和 ROLLBACK命令.而mysql中不少語句是能夠自動提交的.本身翻MySQL documentation
ping一下,確認鏈接是否有效,鏈接池也用.
A ping packet can be sent over a connection using the connection.ping
method. This
method will send a ping packet to the server and when the server responds, the callback
will fire. If an error occurred, the callback will fire with an error argument.
connection.ping(function (err) { if (err) throw err; console.log('Server responded to ping'); })
Every operation takes an optional inactivity timeout option. This allows you to
specify appropriate timeouts for operations. It is important to note that these
timeouts are not part of the MySQL protocol, and rather timeout operations through
the client. This means that when a timeout is reached, the connection it occurred
on will be destroyed and no further operations can be performed.
// Kill query after 60s connection.query({sql: 'SELECT COUNT(*) AS count FROM big_table', timeout: 60000}, function (err, rows) { if (err && err.code === 'PROTOCOL_SEQUENCE_TIMEOUT') { throw new Error('too long to count table rows!'); } if (err) { throw err; } console.log(rows[0].count + ' rows'); });
This module comes with a consistent approach to error handling that you should
review carefully in order to write solid applications.
Most errors created by this module are instances of the JavaScript Error
object. Additionally they typically come with two extra properties:
err.code
: Either a MySQL server error (e.g.'ER_ACCESS_DENIED_ERROR'
), a Node.js error (e.g. 'ECONNREFUSED'
) or an'PROTOCOL_CONNECTION_LOST'
).err.fatal
: Boolean, indicating if this error is terminal to the connectionFatal errors are propagated to all pending callbacks. In the example below, a
fatal error is triggered by trying to connect to an invalid port. Therefore the
error object is propagated to both pending callbacks:
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 });
Normal errors however are only delegated to the callback they belong to. So in
the example below, only the first callback receives an error, the second query
works as expected:
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 });
Last but not least: If a fatal errors occurs and there are no pending
callbacks, or a normal error occurs which has no callback belonging to it, the
error is emitted as an 'error'
event on the connection object. This is
demonstrated in the example below:
connection.on('error', function(err) { console.log(err.code); // 'ER_BAD_DB_ERROR' }); connection.query('USE name_of_db_that_does_not_exist');
Note: 'error'
events are special in node. If they occur without an attached
listener, a stack trace is printed and your process is killed.
tl;dr: This module does not want you to deal with silent failures. You
should always provide callbacks to your method calls. If you want to ignore
this advice and suppress unhandled errors, you can do this:
// I am Chuck Norris: connection.on('error', function() {});
This module is exception safe. That means you can continue to use it, even if
one of your callback functions throws an error which you're catching using
'uncaughtException' or a domain.
For your convenience, this driver will cast mysql types into native JavaScript
types by default. The following mappings exist:
Note text in the binary character set is returned as Buffer
, rather
than a string.
It is not recommended (and may go away / change in the future) to disable type
casting, but you can currently do so on either the connection:
var connection = require('mysql').createConnection({typeCast: false});
Or on the query level:
var options = {sql: '...', typeCast: false}; var query = connection.query(options, function(err, results) { });
You can also pass a function and handle type casting yourself. You're given some
column information like database, table and name and also type and length. If you
just want to apply a custom type casting to a specific type you can do it and then
fallback to the default. Here's an example of converting TINYINT(1)
to boolean:
connection.query({ sql: '...', typeCast: function (field, next) { if (field.type == 'TINY' && field.length == 1) { return (field.string() == '1'); // 1 = true, 0 = false } return next(); } });
WARNING: YOU MUST INVOKE the parser using one of these three field functions in your custom typeCast callback. They can only be called once. (see #539 for discussion)
field.string() field.buffer() field.geometry()
are aliases for
parser.parseLengthCodedString() parser.parseLengthCodedBuffer() parser.parseGeometryValue()
You can find which field function you need to use by looking at: RowDataPacket.prototype._typeCast
If, for any reason, you would like to change the default connection flags, you
can use the connection option flags
. Pass a string with a comma separated list
of items to add to the default flags. If you don't want a default flag to be used
prepend the flag with a minus sign. To add a flag that is not in the default list,
just write the flag name, or prefix it with a plus (case insensitive).
Please note that some available flags that are not supported (e.g.: Compression),
are still not allowed to be specified.
The next example blacklists FOUND_ROWS flag from default connection flags.
var connection = mysql.createConnection("mysql://localhost/test?flags=-FOUND_ROWS");
The following flags are sent by default on a new connection:
CONNECT_WITH_DB
- Ability to specify the database on connection.FOUND_ROWS
- Send the found rows instead of the affected rows as affectedRows
.IGNORE_SIGPIPE
- Old; no effect.IGNORE_SPACE
- Let the parser ignore spaces before the (
in queries.LOCAL_FILES
- Can use LOAD DATA LOCAL
.LONG_FLAG
LONG_PASSWORD
- Use the improved version of Old Password Authentication.MULTI_RESULTS
- Can handle multiple resultsets for COM_QUERY.ODBC
Old; no effect.PROTOCOL_41
- Uses the 4.1 protocol.PS_MULTI_RESULTS
- Can handle multiple resultsets for COM_STMT_EXECUTE.RESERVED
- Old flag for the 4.1 protocol.SECURE_CONNECTION
- Support native 4.1 authentication.TRANSACTIONS
- Asks for the transaction status flags.In addition, the following flag will be sent if the option multipleStatements
is set to true
:
MULTI_STATEMENTS
- The client may send multiple statement per query orThere are other flags available. They may or may not function, but are still
available to specify.
If you are running into problems, one thing that may help is enabling the
debug
mode for the connection:
var connection = mysql.createConnection({debug: true});
This will print all incoming and outgoing packets on stdout. You can also restrict debugging to
packet types by passing an array of types to debug:
var connection = mysql.createConnection({debug: ['ComQueryPacket', 'RowDataPacket']});
to restrict debugging to the query and data packets.
If that does not help, feel free to open a GitHub issue. A good GitHub issue
will have:
The test suite is split into two parts: unit tests and integration tests.
The unit tests run on any machine while the integration tests require a
MySQL server instance to be setup.
$ FILTER=unit npm test
Set the environment variables MYSQL_DATABASE
, MYSQL_HOST
, MYSQL_PORT
,
MYSQL_USER
and MYSQL_PASSWORD
. MYSQL_SOCKET
can also be used in place
of MYSQL_HOST
and MYSQL_PORT
to connect over a UNIX socket. Then run
npm test
.
For example, if you have an installation of mysql running on localhost:3306
and no password set for the root
user, run:
$ 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
https://npm.taobao.org/package/mysql