萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

Node.js 操作 MySQL 完整指南:mysql2 套件從入門到進階

前言

Node.js 搭配 MySQL 是經典的全端開發組合。早期常用 db-mysql,但因依賴 node-waf 已不可用。目前主流推薦官方維護的 mysql2 (或 mysql) 套件,純 JavaScript 實現、MIT 授權、效能優異。本文基於官方文檔整理完整使用指南。

安裝與基礎連線

npm install mysql2
# 或 npm install mysql

建立單一連線

const mysql = require('mysql2');
const connection = mysql.createConnection({
    host: 'localhost',
    user: 'me',
    password: 'secret',
    database: 'my_db'
});

// 連線並處理錯誤
connection.connect((err) => {
    if (err) {
        console.error('連線失敗:', err.stack);
        return;
    }
    console.log('已連線,threadId:', connection.threadId);
});

// 查詢範例
connection.query('SELECT 1 + 1 AS solution', (err, rows, fields) => {
    if (err) throw err;
    console.log('結果:', rows[0].solution);
});

// 優雅關閉
connection.end((err) => { /* 連線已終止 */ });

連線配置選項完整表

選項說明預設值
host資料庫主機名localhost
port連線埠口3306
socketPathUnix Socket 路徑 (優先於 host/port)-
user/password認證憑證-
database預設資料庫-
charset字元集 (SQL 層面稱 collation)UTF8_GENERAL_CI
timezone日期儲存時區local
connectTimeout連線逾時毫秒數10000
supportBigNumbers支援 BIGINT/DECIMAL 大數值false
bigNumberStrings大數值以字串返回 (需配合 supportBigNumbers)false
dateStrings日期型別以字串返回而非 Date 物件false
multipleStatements允許單次查詢多條 SQL (防注入風險)false
sslSSL 配置物件或 profile 名稱-

連線字串 (URI) 格式

const connection = mysql.createConnection(
    'mysql://user:pass@host/db?debug=true&charset=BIG5_CHINESE_CI&timezone=-0700'
);

連線池

生產環境強烈建議使用連線池,自動管理連線生命週期:

const pool = mysql.createPool({
    connectionLimit: 10,
    host: 'example.org',
    user: 'bob',
    password: 'secret',
    database: 'my_db'
});

// 直接查詢 (自動取用/釋放連線)
pool.query('SELECT 1+1 AS solution', (err, rows) => {
    if (err) throw err;
    console.log(rows[0].solution);
});

// 手動取用連線 (需手動釋放)
pool.getConnection((err, connection) => {
    if (err) throw err;
    connection.query('SELECT * FROM sometable', (err, rows) => {
        connection.release(); // 歸還連線池
        if (err) throw err;
        console.log(rows);
    });
});

// 關閉連線池
pool.end((err) => { /* 所有連線已關閉 */ });

連線池專屬選項

選項說明預設值
acquireTimeout取得連線逾時10000
waitForConnections滿載時排隊等待 vs 立即報錯true
connectionLimit最大連線數10
queueLimit排隊上限 (0=無限制)0

連線池事件

pool.on('connection', (conn) => {
    conn.query('SET SESSION auto_increment_increment=1');
});
pool.on('enqueue', () => console.log('等待可用連線...'));

查詢進階用法

參數化查詢 (防 SQL 注入)

// 佔位符 ? 陣列對應
connection.query('SELECT * FROM books WHERE author = ?', ['David'], callback);

// 物件對應 (自動轉義鍵值)
const post = {id: 1, title: 'Hello MySQL'};
connection.query('INSERT INTO posts SET ?', post, callback);
// 產生: INSERT INTO posts SET `id`=1, `title`='Hello MySQL'

// 識別符轉義 (資料表/欄位名)
const sorter = 'date';
connection.query('SELECT * FROM posts ORDER BY ??', [sorter], callback);
// 產生: SELECT * FROM posts ORDER BY `date`

查詢選項物件

connection.query({
    sql: 'SELECT * FROM books WHERE author = ?',
    timeout: 40000,
    values: ['David']
}, callback);

預編譯查詢

const sql = 'SELECT * FROM ?? WHERE ?? = ?';
const inserts = ['users', 'id', userId];
const formatted = mysql.format(sql, inserts);
// 產生安全查詢字串

自定義查詢格式化

connection.config.queryFormat = (query, values) => {
    if (!values) return query;
    return query.replace(/:(\w+)/g, (txt, key) => {
        if (values.hasOwnProperty(key)) return connection.escape(values[key]);
        return txt;
    });
};
connection.query('UPDATE posts SET title = :title', {title: 'Hello MySQL'});

查詢結果處理

取得自增 ID

connection.query('INSERT INTO posts SET ?', {title: 'test'}, (err, result) => {
    if (err) throw err;
    console.log('新增 ID:', result.insertId);
});

影響/變更行數

// 影響行數 (含未實際變更的符合條件行)
connection.query('DELETE FROM posts WHERE title="wrong"', (err, result) => {
    console.log('刪除行數:', result.affectedRows);
});

// 實際變更行數 (排除值未改變的更新)
connection.query('UPDATE posts SET ...', (err, result) => {
    console.log('變更行數:', result.changedRows);
});

串流查詢大量資料

const query = connection.query('SELECT * FROM huge_table');
query.on('fields', (fields) => { /* 欄位元資料 */ })
    .on('result', (row) => {
        connection.pause(); // 暫停接收,處理 I/O 密集任務
        processRow(row, () => connection.resume());
    })
    .on('end', () => { /* 完成 */ })
    .on('error', (err) => { /* 錯誤處理 */ });

// 或使用 Stream2 管道
connection.query('SELECT * FROM posts')
    .stream({highWaterMark: 5, objectMode: true})
    .pipe(writableStream);

進階功能

多語句查詢

const connection = mysql.createConnection({multipleStatements: true});
connection.query('SELECT 1; SELECT 2', (err, results) => {
    console.log(results[0]); // [{1: 1}]
    console.log(results[1]); // [{2: 2}]
});

預存程序

呼叫方式與一般查詢相同,若返回多結果集,結構同多語句查詢。

JOIN 同名欄位處理

// 巢狀結構
const options = {sql: '...', nestTables: true};
// 結果: [{table1: {fieldA:...}, table2: {fieldA:...}}]

// 分隔符合併
const options = {sql: '...', nestTables: '_'};
// 結果: [{table1_fieldA:..., table2_fieldA:...}]

交易控制

connection.beginTransaction((err) => {
    if (err) throw err;
    connection.query('INSERT INTO posts SET title=?', [title], (err, result) => {
        if (err) return connection.rollback(() => { throw err; });
        const log = 'Post ' + result.insertId + ' added';
        connection.query('INSERT INTO log SET data=?', [log], (err, result) => {
            if (err) return connection.rollback(() => { throw err; });
            connection.commit((err) => {
                if (err) return connection.rollback(() => { throw err; });
                console.log('交易成功!');
            });
        });
    });
});

連線健檢與逾時

// Ping 檢查
connection.ping((err) => { if (err) throw err; console.log('伺服器回應'); });

// 查詢層級逾時 (60秒)
connection.query({sql: 'SELECT COUNT(*) FROM big_table', timeout: 60000}, (err, rows) => {
    if (err && err.code === 'PROTOCOL_SEQUENCE_TIMEOUT')
        throw new Error('查詢逾時!');
    console.log(rows[0].count);
});

錯誤處理機制

  • 錯誤代碼:err.code (MySQL 錯誤如 ER_ACCESS_DENIED_ERROR、Node 錯誤如 ECONNREFUSED、內部錯誤如 PROTOCOL_CONNECTION_LOST)
  • 致命錯誤:err.fatal=true 表示連線已不可用,會傳播給所有待決回調
  • 非致命錯誤:僅傳給對應回調
  • 無監聽器的 error 事件:會拋出堆疊並殺死進程,建議總是提供回調或註冊 connection.on('error', ...)

型別轉換對照表

JS 型別MySQL 型別
NumberTINYINT, SMALLINT, INT, MEDIUMINT, YEAR, FLOAT, DOUBLE
DateTIMESTAMP, DATE, DATETIME
BufferTINYBLOB, MEDIUMBLOB, LONGBLOB, BLOB, BINARY, VARBINARY, BIT
StringCHAR, VARCHAR, TINYTEXT, MEDIUMTEXT, LONGTEXT, TEXT, ENUM, SET, DECIMAL, BIGINT, TIME, GEOMETRY

可透過 typeCast: false 關閉自動轉換,或自訂 typeCast 函數 (如將 TINYINT(1) 轉 Boolean)。

除錯與測試

// 開啟除錯模式
const connection = mysql.createConnection({debug: true});
// 或限定封包類型
const connection = mysql.createConnection({debug: ['ComQueryPacket', 'RowDataPacket']});

單元測試:FILTER=unit npm test;整合測試需設定環境變數 MYSQL_HOST、MYSQL_DATABASE 等後執行 npm test。

copyright © 萬盛學電腦網 all rights reserved