node.jsでMySqlのDBを操作する機会があったので備忘録としてまとめます。
まずTypeScriptのコンパイル環境をつくる
完全に好みが分かれると思いますが、TypeScriptで書きたくなりました。
npm i -g typescript
npm i -g tsc
下準備
DB操作は非同期処理になります。
TypeScriptで async/await
が使えるようにしておきます。
{
"compilerOptions": {
"lib": ["es2015"]
}
mysqljs
nodeでのMySQL操作はこちらを利用しました。
https://github.com/mysqljs/mysql
npm i --save mysql @types/mysql
ハマりポイント
非同期処理
基本的なDB操作は公式のとおりなので割愛しますが、
複数の更新操作をする場合、前の更新が終わってから次の更新を行う必要があると思います。
先ほど下準備した async/await
の出番、と思ってこう書きました。
import * as mysql from 'mysql';
// DB接続情報を定義
export interface DbConfig {
host: string;
user: string;
password: string;
database: string;
}
const dbConfig: DbConfig = {
host: 'HOST_NAME',
user:'USER_NAME',
password:'PW',
database:'DB'
};
// 接続
const pool = mysql.createPool(dbConfig);
/** INSERT用のasync関数 */
async function insertRecord(tableName: string, data: any) {
try {
const sql: string = mysql.format(`INSERT INTO ${tableName} SET ?`, data);
return await pool.query(sql) as any;
} catch (err) {
pool.end();
throw new Error(err);
}
}
(async () => {
try{
// 挿入するレコードを準備
const insertRecord: any = {
name: 'hoge'
};
// 挿入処理
const result = await insertRecord('TABLE_NAME', insertRecord);
// 挿入結果のレコードIDをコンソール出力
console.log(result.insertId);
pool.end();
} catch (err) {
pool.end();
throw new Error(err);
}
})();
でも結果は、レコードは挿入されますが、コンソール出力はされませんでした。
原因
mysqljsライブラリのpool.query
がPromiseを返してくれない
pool.query
はPromiseを返してくれないので、非同期処理が終わっても待ち状態のままになります。
下の3行追加することで解決しました。
import * as util from 'util';
const pool = mysql.createPool(dbConfig);
// @ts-ignore
pool.query = util.promisify(pool.query);
pool.query = util.promisify(pool.query);
とすることで、 Promise
を返すように上書きしています。
※TypeScriptだとコンパイルのときに型のエラーがでたのでスルーしてもらうために @ts-ignore
を1行上に追加しています。
そのほかのハマりポイント
TypeScriptのビルド
すごくしょうもないですが、 index.ts
にいろいろ書いて、下記のようにファイル名を指定してビルドを実行するとtsconfig.json
の内容が反映されません。
# ファイル指定はだめ
tsc index.ts
# OK
tsc
forEachはPromiseをみてくれない
forEachの中でasync functionをつかっても、Promiseのリターンを待たずにつぎにいってしまいます。
forEach自体にasyncとかつけても変わりません。
おとなしくfor文を使いました。
最後に
nodeでMySQLですが、挿入したレコードのidを取得して別のレコードのカラムを更新するのが楽にできて便利でした。
トランザクションロールバックの処理まで記事にしようと思いましたが、長くなったので書いたコードだけ上げておきます。
/**
* INSERT,UPDATE,DELETEなど、
* エラー時のTransaction Rollbackを必要とするsqlの発行に使用 */
async function editRecordOfTransactionRollback(sql: string) {
console.log(sql);
return await new Promise((resolve => {
pool.getConnection((err, connection) => {
if (err) {
connection.release();
pool.end();
throw err;
}
connection.beginTransaction(err1 => {
if (err1) {
connection.release();
pool.end();
throw err1;
}
connection.query(sql, (error, results) => {
if (error) {
connection.rollback(() => {
connection.release();
pool.end();
throw error;
});
} else {
connection.commit((err2 => {
if (err2) {
connection.rollback(() => {
connection.release();
pool.end();
throw err2;
});
} else {
connection.release();
resolve(results);
}
}));
}
});
});
});
})) as Promise<any>;
}