自 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
Node.js 的編程接口模塊是 hdb,能夠用 npm install hdb
安裝。Github 的源碼地址爲:node-hdb。有示例和說明,容易學習。github
本文打算介紹兩個方面: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 語句的參數。注意這個參數是數組類型,即便只有一個參數,也要使用數組。
使用 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" }
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 端口。
保存數據庫的配置信息,是一個對象:
module.exports = { hana:{ host: '192.168.2.100', port: 30015, user: 'STONE', password: 'pwd' } };
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); }); }); }); };
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); };
在項目文件下,經過 node server.js
啓動服務。而後打開 Postman 進行測試。如下是部分截圖。