IBM i で node.js を使ってみよう!!(3)- SQL でデータベースにアクセス -

前回に引き続き「Native JavaScript applications on IBM i with Node.js」を参考にしてテストしてみた結果の紹介です。


SQL とコマンドの実行

では SQL とコマンドを実行する処理を追加してみましょう。

こちらが全文です。前回のものと比較して何が追加されたか見てみてください。UTF-8 を指定して保管することを忘れないようにしてくださいね。

sample_process_sql_or_cmd.js

var http = require('http');
var ip = "x.x.x.x";
var port = 8888;

var fs = require('fs');
var url = require('url'); 

var db = require('/QOpenSys/QIBM/ProdData/Node/os400/db2i/lib/db2');
var xt = require('/QOpenSys/QIBM/ProdData/Node/os400/xstoolkit/lib/itoolkit');

var DBname = "*LOCAL ";

http.createServer(function(req, res) {

   var realPath = __dirname + url.parse(req.url).pathname;
   fs.exists(realPath, function(exists){ 
      if(!exists){ 

         var sql = url.parse(req.url, true).query.sql;
         var cmd = url.parse(req.url, true).query.cmd;

         res.writeHead(200, {'Content-Type': 'text/plain'});
 
         if(sql && sql.length > 0) {
            console.log("SQL statement : " + sql);
            db.init();
            db.conn(DBname);
            db.exec(sql, function(rs) {
               res.write(JSON.stringify(rs)); 
             });
            db.close();
          } 
          // SQL Process ends 

         if(cmd && cmd.length > 0) {
            console.log("CL command : " + cmd); 
            var conn = new xt.iConn(DBname);
            conn.add(xt.iSh("system -i " + cmd)); 
            function cb(str) {
               res.write(xt.xmlToJson(str)[0].data);
             } 
            conn.run(cb); 
          } 
          // Command Process ends

         res.end();

       } else { 
          // html not exists
 
          var file = fs.createReadStream(realPath); 
          res.writeHead(200, {'Content-Type': 'text/html'}); 
          file.on('data', res.write.bind(res)); 
          file.on('close', res.end.bind(res));  
          file.on('error', function(err){ 
          res.writeHead(500, {'Content-Type': 'text/plain'}); 
          res.end("500 Internal Server Error"); 

              }); 
              // end of else and if exists
       } 
       // end of funcion(exists)
    }); 
    // end of fs.exists

}).listen(port, ip);
console.log('Server running');

↓ のように実行します。ちなみに、↓ の実行ジョブの CCSID は 1399 です。5035 でももちろん実行できました。

SQL の実行

↓ のように SQL を入力して実行させてみると…

↓ のように結果が返ってきます。

SQL の処理

SQL が入力されたときの処理は ↓ の部分です。

var db = require('/QOpenSys/QIBM/ProdData/Node/os400/db2i/lib/db2');

         var sql = url.parse(req.url, true).query.sql;

         if(sql && sql.length > 0) {
            console.log("SQL statement : " + sql);
            db.init();
            db.conn(DBname);
            db.exec(sql, function(rs) {
               res.write(JSON.stringify(rs)); 
             });
            db.close();
          } 
          // SQL Process ends 

サーバ側には ↓ のように、実行された SQL を表示するようになっています。Console.log のところですね。

SQL 自体の実行はごく単純で、init メソッドで初期化、connect メソッドで接続、exec メソッドで SQL の実行、close メソッドで接続の終了、ということになっています。

ほぼ同じような例が「DB2 Access from Node.JS」という記事にも載っていますので、参考にしてみてください。

日本語データの表示

もちろん、日本語のデータも表示できます。ブラウザの文字コードを UTF-8 にしないと文字化けしますので注意してくださいね。

DB2 for i アクセス API

各API のより詳しいパラメータなどは「DB2 for i access APIs」というページに載っていますので、見てみてください。

一例を挙げましょう。

init メソッドではデータベースアクセスをしているセッションの属性をセットすることができます。

conn メソッドでは特定のユーザー/パスワードを使用してデータベースへの接続を行うこともできます。

それぞれ、↓ の例を見てみてください。

        var mode = db.SQL_TRUE;

        if(sql && sql.length > 0) {                          
           console.log("SQL statement : " + sql);            
           // db.init();                                     
           db.init(function(){                               
              db.setEnvAttr(db.SQL_ATTR_UTF8, mode);          
              db.serverMode(true);                           
           });                                               
           // db.conn(DBname);                               
           db.conn(DBname, 'USRPRF', 'PASSWORD');            
           db.exec(sql, function(rs) {                       
              res.write(JSON.stringify(rs));                 
            });  

SQL サーバーモードでの実行

init メソッドの中の serverMode(true) で、SQL の実行をサーバーモードで実行させることができます。

最初の例では(つまり、サーバーモードでない例では)、PASE シェルを起動したジョブでそのまま SQL が実行されるようになっています。
サーバーモードにすると、この SQL が QSQSRVR ジョブで実行されるようになるんですね。

SQL 実行時の環境属性の設定

init メソッドの中の setEnvAttr() では、環境属性の設定を行うことができます。

SQL_ATTR_UTF8 は、文字コードの処理をジョブの CCSID で行うのではなく、UTF-8 として扱うようにセットします。

ユーザー/パスワードを使用しての接続

最初の例では、conn メソッドではデータベース名(*LOCAL)のみを引数にしてデータベースとの接続を行っていますね。db.conn(DBname) といったように。
その場合、実行ジョブのユーザープロファイルがそのまま使われます。

conn(DBname, 'USRPRF', 'PASSWORD') と引数を追加して、ユーザー名、パスワードを指定した接続を行うこともできるようになっています。

JSON 形式のデータを HTML テーブル形式にして表示する

ここまでの例では、SQL で取得したデータは JSON 形式でそのまま表示していましたが、もちろん他のかたちで表示させることができます。

SQL の処理を行う部分は ↓ のようになります。テーブル形式にできることを確認したかったため、ちょっと急いだコーディングをしてしまい、あまりスマートとは言えないコードになっていますが、参考にはなりますよね。

var db = require('/QOpenSys/QIBM/ProdData/Node/os400/db2i/lib/db2');

         var sql = url.parse(req.url, true).query.sql;

         if(sql && sql.length > 0) {
            console.log("SQL statement : " + sql);
            var mode = db.SQL_TRUE;
            db.init(function(){
               db.setEnvAttr(db.SQL_ATTR_UTF8,mode);
               db.serverMode(true);
            });
            db.conn(DBname);
            db.exec(sql, function(rs) {
               var html = '<table border="1">';
               var data = '{ "file" :' + JSON.stringify(rs) + '}';
               data = eval ("(" + data + ")");
               html = html + '<tr>';
               html = html + '<td>CUSNUM</td><td>LSTNAM</td><td>INIT</td><td>STREET</td><td>BALDUE</td>';
               html = html + '</tr>';
               for(var i=0;i < Object.keys(data.file).length; i++){
                  html = html + '<tr>';
                  html = html + '<td>' + data.file[i].CUSNUM + '</td>';
                  html = html + '<td>' + data.file[i].LSTNAM + '</td>';
                  html = html + '<td>' + data.file[i].INIT   + '</td>';
                  html = html + '<td>' + data.file[i].STREET + '</td>';
                  html = html + '<td>' + data.file[i].BALDUE + '</td>';
                  html = html + '</tr>';
               }
               html = html + '</table>'
               res.write(html);
             });
            db.close();
          } 
          // SQL Process ends 

もともとのコードでは ↓ の一行だったものが

               res.write(JSON.stringify(rs)); 

↓ になっています。

               var html = '<table border="1">';
               var data = '{ "file" :' + JSON.stringify(rs) + '}';
               data = eval ("(" + data + ")");
               html = html + '<tr>';
               html = html + '<td>CUSNUM</td><td>LSTNAM</td><td>INIT</td><td>STREET</td><td>BALDUE</td>';
               html = html + '</tr>';
               for(var i=0;i < Object.keys(data.file).length; i++){
                  html = html + '<tr>';
                  html = html + '<td>' + data.file[i].CUSNUM + '</td>';
                  html = html + '<td>' + data.file[i].LSTNAM + '</td>';
                  html = html + '<td>' + data.file[i].INIT   + '</td>';
                  html = html + '<td>' + data.file[i].STREET + '</td>';
                  html = html + '<td>' + data.file[i].BALDUE + '</td>';
                  html = html + '</tr>';
               }
               html = html + '</table>'
               res.write(html);

JSON 形式で受け取った SQL の実行結果は行を表現しています。
↓ のように SQL テーブルをあらわす記述を追加したうえで、eval 関数を使って JavaScript オブジェクトに変換します。

               var data = '{ "file" :' + JSON.stringify(rs) + '}';
               data = eval ("(" + data + ")");

SQL テーブルをあらわす記述をキーにして、配列の繰り返し数を ↓ のように Obejct.keys().length で取得し、ループで各要素を処理します。

               for(var i=0;i < Object.keys(data.file).length; i++){
                  html = html + '<tr>';
                  html = html + '<td>' + data.file[i].CUSNUM + '</td>';

特別なトリックなどはまったく使っていないので、それほど難しくはないと思います。
他にもいろんなやり方もあると思いますし、変換先もさまざまでしょう。

もともと、柔軟に対応させるためにデータ取得結果の格納先を JSON 形式にしているわけです。様々な利用ができると思いますので、いろいろ調べて試してみてくださいね。


次回は node.js のプログラムの中からの IBM i コマンドの実行について紹介したいと思います。

[Top Pageに戻る]

Ads by TOK2