利用 Node.js 實現 SAP Hana 數據庫編程接口

自 SAP HANA SP 11 以後,可使用 Node.js 做爲 Hana 的編程接口。SAP 將 Application server 簡稱爲 XS。如今 XS 已經演化爲 Advanced 版本。爲了區別,早期的 XS 被稱爲 XS Classical。node

從下圖能夠看出,和 Hana DB 進行交互的有 HANA XS Classical 、Hana Cloud Platform (HCP) 和 XS Advanced。而可以運行在 HCP 和 XS Advanced 的編程接口包括 XSJS (SAP 推出的服務器端 JavaScript,但目前看,社區並不活躍)、Node.js、Tomcat / TomEE (Java 應用程序編寫)等。最近測試了 Node.js 編程接口,感受還不錯。git

hdb 模塊

Node.js 的編程接口模塊是 hdb,能夠用 npm install hdb 安裝。Github 的源碼地址爲:node-hdb。有示例和說明,容易學習。github

hdb CRUD

本文打算介紹兩個方面:sql

  • hdb CRUD 的基本方法;數據庫

  • 以及如何利用 Node.js 的 express 框架實現 REST 風格 API (Restful API)。express

先看看基本使用方法:npm

var hdb = require('hdb')

var client = hdb.createClient({
    host: '192.168.2.100,
    port: 30015,
    user: 'STONE',
    password: 'pwd'
});

client.connect(function(err){
    if (err){
        return console.error('Connect error', err);
    }
    var sql = 'SELECT * FROM STONE.EMP_MASTER';
    client.exec(sql, function(err, rows){
        if (err){
            return console.error('Execute error', err);
        }
        console.log('Results:', rows);
    });
});

和前幾篇同樣,仍然使用 STONE.EMP_MASTER 做爲數據源。咱們注意到,Node.js 普遍使用異步回調函數。使用異步的緣由是 : Node.js 是單線程的,經過異步來避免阻塞 (blocking)。好比,從Hana 數據庫查詢 employees 表,但不知道須要多久能得到查詢結果,經過異步機制,數據查詢到以後放在 rows 中。編程

上面的 SQL 語句沒有參數。下面經過 insert 語句來講明帶參數 SQL 語句的處理方法。json

client.connect(function(err){
    if (err){
        return console.error('Connect error', err);
    }

    var sql = 'INSERT INTO STONE.EMP_MASTER VALUES(?,?,?,?,?,?,?,?)';

    client.prepare(sql, function(err, statement){
        if (err){
            return console.error('Prepare error:', err);
        }

        var params = ['9001','Male',18,'test4@qq.com','13800-138000','Bachelor','Married',1];

        statement.exec(params, function(err, affectedRows){
            if (err){
                return console.error('Execute error:', err);
            }
            console.log('Affected rows:', affectedRows);
        });
    });
});
  • client.prepare() 先處理語句,成功後放在 statement數組

  • statement.exec() 語句執行查詢,函數的第一個參數是 SQL 語句的參數。注意這個參數是數組類型,即便只有一個參數,也要使用數組。

提供 REST 風格的 Service

使用 Node.js 的 express 框架來實現。網上有很是多使用 express 建立 REST 風格 API 的教程,這裏就不細說步驟了。後面會介紹怎樣在 OpenUI5 中經過 Rest Service 來對對數據庫進行增刪改查。

  • 安裝 Node.js

  • 建立一個文件夾,在文件夾中運行 npm init 建立 packages.json 文件。

  • 安裝 express,這裏提供一種方法: npm install express --save--save 參數會修改 packages.json 文件。

  • 安裝 body-parser。這個模塊將處理 post 請求,對 post 請求進行解析。

工程的文件結構以下:

主要文件有:

  • server.js : 啓動服務
  • dbconfig.js: hana 數據庫鏈接的配置信息
  • emp.controller.js: emp_master 表增刪改查
  • emp_routes.js: 路由管理

先說明 package.json 文件,管理 app 依賴的模塊:

{
  "name": "hana_app",
  "version": "1.0.0",
  "description": "hana in nodejs + express",
  "main": "server.js",
  "dependencies": {
    "body-parser": "^1.18.2",
    "express": "^4.16.2",
    "hdb": "^0.15.2"
  },
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Stone Wang",
  "license": "MIT"
}

server.js

var express = require('express');
var bodyParser = require('body-parser');

var app = express();

// parse requests of content-type - application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({extended: true}));

// parse requests of content-type - application/json
app.use(bodyParser.json());

// home page
app.get('/', function(req, res){
    res.json('Welcome.');
});

// register routes
var route = require('./app/routes/emp.routes.js')
route(app);

// listen on port 3000
app.listen(3000, function(){
    console.log('Server is running on port 3000.');
});

server.js 中定義首頁的響應,註冊路由以及偵聽 3000 端口。

dbconfig.js

保存數據庫的配置信息,是一個對象:

module.exports = {
    hana:{
        host: '192.168.2.100',
        port: 30015,
        user: 'STONE',
        password: 'pwd'
    }
};

emp.controller.js

var hdb = require("hdb");
var dbconfig = require("../../config/dbconfig.js");

var client = hdb.createClient(dbconfig.hana);

// list all
exports.listAll = function(req, res){
    var sql = "SELECT * FROM STONE.EMP_MASTER";

    client.connect(function(err){
        if (err){
            res.send({"error": err.message});
        }
        client.exec(sql, function(err, rows){
            if (err){
                res.send({"error": err.message});
            }
            client.end();
            res.send({rows});
        });
    })
};

// query by id
exports.queryById = function(req, res){
    var sql = "SELECT * FROM STONE.EMP_MASTER WHERE EMP_ID=?";
    client.connect(function(err){
        if (err){
            res.send({"error": err.message});
        }
        client.prepare(sql, function(err, statement){
            if (err){
                res.send({"error": err.message});
            }
            statement.exec([req.params.emp_id], function(err, rows){
                if (err){
                    res.send({"error": err.message});
                }
                client.end();
                res.send({rows});
            });
        });
    });
};

// create
exports.create = function(req, res){
    var sql = "INSERT INTO STONE.EMP_MASTER VALUES(?,?,?,?,?,?,?,?)";
    client.connect(function(err){
        if (err){
            res.send({"error": err.message});
        }
        client.prepare(sql, function(err, statement){
            if (err){
                res.send({"error": err.message});
            }

            var params = [
                req.body.EMP_ID,
                req.body.GENDER,
                req.body.AGE,
                req.body.EMAIL,
                req.body.PHONE_NR,
                req.body.EDUCATION,
                req.body.MARITAL_STAT,
                req.body.NR_OF_CHILDREN
            ];
            statement.exec(params, function(err, data){
                if (err){
                    res.send({"error": err.message});
                }
                client.end();
                res.sendStatus(200);
            });
        });
    });
};

// update
exports.update = function(req, res){
    var sql = "UPDATE STONE.EMP_MASTER SET GENDER=?, AGE=?, EMAIL=?, PHONE_NR=?, EDUCATION=?, MARITAL_STAT=?, NR_OF_CHILDREN=? WHERE EMP_ID=?";
    client.connect(function(err){
        if (err){
            res.send({"error": err.message});
        }
        client.prepare(sql, function(err, statement){
            if (err){
                res.send({"error": err.message});
            }

            var params = [       
                req.body.GENDER,
                req.body.AGE,
                req.body.EMAIL,
                req.body.PHONE_NR,
                req.body.EDUCATION,
                req.body.MARITAL_STAT,
                req.body.NR_OF_CHILDREN,
                req.params.emp_id
            ];

            statement.exec(params, function(err, data){
                if (err){
                    res.send({"error": err.message});
                }
                client.end();
                res.sendStatus(200);
            });
        });
    });
};

// delete
exports.delete = function(req, res){
    var sql = "DELETE FROM STONE.EMP_MASTER WHERE EMP_ID=?";
    client.connect(function(err){
        if (err){
            res.send({"error": err.message});
        }
        client.prepare(sql, function(err, statement){
            if (err){
                res.send({"error": err.message});
            }

            statement.exec([req.params.emp_id], function(err, data){
                if (err){
                    res.send({"error": err.message});
                }
                client.end();
                res.sendStatus(200);
            });
        });
    });
};

emp.routes.js

module.exports = function(app){
    var empController = require("../controllers/emp.controller.js");

    // list all
    app.get('/employees', empController.listAll);

    // query by ID
    app.get('/employee/:emp_id', empController.queryById);

    // create
    app.post('/employee/create', empController.create);

    // update
    app.put('/employee/:emp_id',empController.update);

    // delete
    app.delete('/employee/:emp_id', empController.delete);
};

使用 Postman 測試

在項目文件下,經過 node server.js 啓動服務。而後打開 Postman 進行測試。如下是部分截圖。

  • listAll

  • create

  • update

  • delete

  • update
相關文章
相關標籤/搜索