Node.jsによる MySQLの操作
同期処理 / SQL文字列の変数展開
macOS 10.15.7 / MySQL Server 5.7.16
はじめに
MySQLモジュールの queryメソッドは非同期処理である。処理の制御は SQLコマンドを実行する queryメソッドの終了を待たずに次のステートメントに移る。
テーブル
クエリーの結果を受けて行う処理は、queryメソッドのコールバック関数の中に記述しなくてはならない。ひとつの単純な処理ならいいが、例えば、前の処理の結果をもとに複数の SQL文を実行するといった場合、処理のネストが深くなる。また、コールバック関数の中でデータを編集するために外部で定義した変数への参照が増えるとコードの視認性が落ちることになる。
できることなら queryメソッドの終了を待ち、結果を受け取り、そこから処理を続けたい。そうすればかなりスッキリしたコードになるだろう。Promiseオブジェクトおよび、async & await 文を使用することにより、そのような同期的な処理を実現することができる。
同期処理
関数(db_query)を作成し、SQLステートメント(connection.queryメソッド)を実装する。関数は SQLステートメントが終了するまで待ち、結果は Promiseオブジェクトに格納する。これを戻り値として呼び出し元に返す。
実装のポイントは次の通り。
- 関数は async(非同期) とする、
- 非同期な処理(ここでは SQLステートメントの実行)は、Promiseオブジェクトのコールバック関数に記述し、これを await演算子をつけて実行する。
- 結果はコールバック関数の resolveメソッドにより Promiseオブジェクトに格納される。
- 関数の飛び出し元は、Promiseオブジェクトを戻り値に受け取る。thenメソッドで処理の結果を取得することができる。
- コールバック関数の rrejectメソッドにより例外を返すことができる。
SQLステートメントを変数で置き換える
下の例では、SELECTステートメントの WHERE句の条件値(idの値)に関数のパラメータ値を指定している。
connection.queryメソッドの第2引数に SQLステートメントに展開する値を配列として設定する。値は、SQLステートメントに記述されたリテラル '?' と置き換わる。要素が複数の場合、左側のリテラルからとなる。
ネストしたSQLステートメント
指定したレコードによりテーブルを検索し、レコードが存在すれば値を更新し、存在しなければ新規に追加する。まずSELECT文の実行する。結果を判定し、UPDATE文あるいいは、INSERT文を実行する。3種類のSQLステートメントのうち二つが実行されることになる。
前記の処理を踏襲して、async関数を3種類作成してもよいが、ここではひとつの async関数の中で全てを行うことにしてみる。実装のポイントは次の通り。
- SQLステートメントを実行する処理を共通関数(queryWrapper)とする。
- connection.queryメソッドに params引数を追加し、SELECT文の WHERE句や INSERT文の VALUE句の値を変数で指定できるようにする。
- async関数の中では、SQLステートメントの結果は、Promiseオブジェクトをアンラップ(then)することなく、そのまま使える。SELECT COUNTステートメントの結果(rows)がそうである。
- async関数の戻り値には、プリミティブな値やオブジェクトを指定することができるが、全て Promiseオブジェクトとして返される。値やオブジェクトは thenメソッドにより取り出す必要がある。
Promiseオブジェクトを受け取らない
SQLステートメントを実行した後の処理を async関数の中で完結させる場合のコード例を示す。例外を補足するために try文で囲む。