|||||||||||||||||||||

なんぶ電子

- 更新: 

Node.jsからMariaDBを操作

Error: Cannot enqueue Query after invoking quit.

以前Node.jsにクローラを設定して、天気予報を取得しました。

今回はそこで取得した天気予報をデータベースに保存しておきたいと考えたので、Node.js経由でMariaDBデータベースに保存します。

Node.jsの初期設定方法はリンク先の記事で紹介しています。ここではパッケージは、mysqlをインストールするところから始めます。

インストールと使い方

まずは既存のNode.jsのプロジェクトにmysqlをインストールします。もし、新規プロジェクトを作成するのなら事前に「nmp init -y」コマンドを実行しておいてください。

PS> npm install mysql

基本的な使い方は先のnpmjsのページから引用して紹介します。

conectionオブジェクトを次の引数を使って作成します。hostにサーバーのアドレス、userに接続ユーザー名、passwordにパスワード、databaseに接続するデータベース名をセットします。他に設定可能な値としてcharset等があります。

connectionオブジェクトは、connect()で接続、end()で切断、query(SQL,function)でSQLの実行をします。queryの第1引数にはSQL文、第2引数にはSQL実行後の関数を渡します。

プレースホルダ(?)を使う場合は、引数がひとつ増え、第2引数にはバインドする値の配列を渡します。配列にはプレースホルダの順に値を入れます。関数を渡す場所はひとつ繰り下がり第3引数になります。

sample.js

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 (error, results, fields) {
  if (error) throw error;
  console.log('The solution is: ', results[0].solution);
});
 
connection.end();

resultsに行が返り、カラム名がプロパティになっています。ここでは一番最初(インデックスが0)の行の、solutionカラムの値をconsole.logで表示しています。

SQL文中では、カラム名の文字の大小は区別されませんが、resultsではカラム名が大文字なら大文字、小文字なら小文字で指定しないと取得できません。

SQL文内のテーブル名に関しては、MySQL側のルールで、テーブル名は大文字小文字を区別するOSで動くサーバーの場合は一致させる必要があり、区別しないOSで動くサーバーの場合はどちらでもかまいません。

トランザクションにも対応しているようで、こちらも公式ページに使い方が次のように掲載されていました。

注意が必要なのはbeginTransactionやquery関数はasync(非同期)だという点です。この関数の外側に続けてconnection.end()と書いてしまうとトランザクションやクエリの途中でコネクションが切断されてしまい「Error: Cannot enqueue Query after invoking quit.」のエラーがでます。

connecion.end()は、エラートラップ時かcommitのコールバック関数内(参考スクリプト内ではconsole.log('success!');の場所です)で実行しましょう。

transaction.js


...
connection.beginTransaction(function(err) {
  if (err) { throw err; }
  connection.query('INSERT INTO posts SET title=?', title, function (error, results, fields) {
    if (error) {
      return connection.rollback(function() {
        throw error;
      });
    }
 
    var log = 'Post ' + results.insertId + ' added';
 
    connection.query('INSERT INTO log SET data=?', log, function (error, results, fields) {
      if (error) {
        return connection.rollback(function() {
          throw error;
        });
      }
      connection.commit(function(err) {
        if (err) {
          return connection.rollback(function() {
            throw err;
          });
        }
        console.log('success!');
      });
    });
  });
});
...

fields

実行時に渡すコールバック関数にたびたび含まれるfieldsですが、これにはカラムの情報が入ってきます。

ひとつのカラムはひとつのFieldPacketで表現され、SQLで指定したカラムの数だけFieldPacketは存在します。fieldsはそのFieldPacketの配列です。なので、結果の列数を取得したければfields.lengthで取得できます。

FieldPacketオブジェクトは次のプロパティをもちます。

  1. catalog
  2. db

    データベース名

  3. table

    SQL文で指定したカラムが所属するテーブル名

  4. orgTable

    オリジナルのテーブル名

  5. name

    SQL文で指定したカラム名

  6. orgName

    オリジナルのカラム名

  7. charsetNr
  8. length

    全体のカラム長。たとえばdecimal(8,2)の時は10が入り、UTF-8のchar(1)の時は3が入ります

  9. type

    カラムのタイプが数値で入ります。decimalは246、charは254です

  10. flags
  11. decimals

    decimal指定時の小数点以下のサイズです。decimal(8,2)の時は2が入ります

  12. default
  13. zeroFill
  14. protocol41

筆者が実際に作成したコード

実際に作成したコードは次のようになりました。

updatedb.js

let mysql  = require('mysql');

let dbRows = getDbRows();//更新用のデータが入った配列を取得します。

//複数行を処理する為に、成功時に再帰呼び出しをします。
function insDbRowTask (intIndex, connection)  {
  if (intIndex < dbRows.length) {
    connection.query('INSERT INTO WEATHER_TABLE VALUES(?,?,?,?,?)',dbRows[intIndex],function (error, results, fields){
      if (error) { throw error; }
      insDbRowTask(intIndex+1,connection);
    });
  } else {
    //最終処理(commit)
    connection.commit(function(err) {
      if (err) {
        //コミットエラー;
        throw err;
      } else {
        connection.end();
      }
    });
  }
}

function updateDb() {
  
  //mysqlオープン
  let connection = mysql.createConnection({
    host     : 'localhost',
    user     : 'admin',
    password : 'admin',
    database : 'test'
  });
  
  connection.connect();
  
  try {
    connection.beginTransaction(function(err) {
      if (err) { throw err; }
      
      insDbRowTask(0, connection);
      
    });
  } catch(e) {
    connection.rollback(function(){
      connection.end();
    });
  }
}

筆者紹介


自分の写真
がーふぁ、とか、ふぃんてっく、とか世の中すっかりハイテクになってしまいました。プログラムのコーディングに触れることもある筆者ですが、自分の作業は硯と筆で文字をかいているみたいな古臭いものだと思っています。 今やこんな風にブログを書くことすらAIにとって代わられそうなほど技術は進んでいます。 生活やビジネスでPCを活用しようとするとき、そんな第一線の技術と比べてしまうとやる気が失せてしまいがちですが、おいしいお惣菜をネットで注文できる時代でも、手作りの味はすたれていません。 提示されたもの(アプリ)に自分を合わせるのでなく、自分の活動にあったアプリを作る。それがPC活用の基本なんじゃなかと思います。 そんな意見に同調していただける方向けにLinuxのDebianOSをはじめとした基本無料のアプリの使い方を紹介できたらなと考えています。

広告