nodejs启动一个简单的服务非常的迅捷,网上大部分数据库都是用的mysql,连接orcale的例子比较年代久远。基于oracle官方提供的node-oracle 模块来进行一次连接oracle,提供简单的查询服务。适用于传统it前端开发环境构建。
首先确保安装成功nodejs,并查看版本,我用的是10.14.2
建立开发目录后,在目录用cmd运行 npm init 进行nodejs项目初始化构建,界面会出现一些帮助构建的信息,一步一步点就行了,成功后,会在该目录下出现node_modules文件夹和package.json配置信息文件。
然后开始安装oracle提供的官方nodejs 模块,cmd命令 输入
npm i oracledb
安装好后可以在package.json中查看版本,我安装的是 3.0.1 版。
接下来一个很重要的环节就是需要oracle的客户端程序。
我们来到oracle的官网(变得好看了官网),然后进入到客户端下载页面
选择对应的开发系统之后,然后进行对应客户端版本的选择。
这块需要注意一下: 一定要选择配合你oracle版本的客户端,如果不知道问一下dba或者项目组成员,一定要选对客户端版本,不然连接很难成功!(血泪教训),我选择的是Version 11.2.0.4.0下面的第一个安装包。
然后下载好了之后安装(推荐安装到刚才构建npm init的同一个目录下)。
弄好了之后,需要配置一下环境变量,在系统环境变量追加你自己的oracle客户端目录,下图是我加的环境变量
到这基本的环境配置就完毕了。接下来开始写代码来连接oracle。
官方的github上面的各种文档非常的详细,issues关闭的1000多个,在编写自己的逻辑如果实在想不通了可以去官方github查查别人提过的问题,或者自己提问,官方作者cjbj和dmcghan会很快回复问题的。
还有,一些具体的配置和说明可以查看官方文档。
开始编码
连接oracle数据库超级简单,你可以采用直接oracledb.getConnection的方法来建立连接,也可以使用连接池,官方的建议是使用连接池。下面是创建一个连接池,并建立连接执行sql的代码(用的async await写法),注意引入oracledb模块。新建server2.js 文件。
var oracledb = require("oracledb");
(async ()=>{
try{
await oracledb.createPool({
_enableStats: true,
user: '你的用户名',
password: '你的密码',
connectString: '你的数据库连接串',
poolAlias: "起一个别名用来识别不同的连接池"
});
var connection = await oracledb.getPool('你起的别名').getConnection();
var result = await connection.execute('select 123 from dual');
await connection.close();
console.log(result)
}catch(err){
console.log(err.message)
}
})();
写好之后打开cmd或者如果你是用vscode开发的,在里面打开终端,用 node server.js 运行刚刚编写的代码,你会看到数据库查询结果的返回值。
到目前为止,我们已经成功创建了连接池,调用连接池获取了连接,并且运行了查询语句,然后记得查询结束之后要关闭连接,把连接释放回连接池。
接下来我们完善我们的代码,用nodejs的http模块,readline模块和刚刚运用到的oracledb模块来组合出一个好玩的前端查询服务器
这个是我为了方便公司前端开发构建的一个开发小服务器,适合开发环境自己开发用,传统it开发,前后台都是糅杂在一起的,有时候后台给的唯一接口就是需要一个sql,然后给返回值,这个简单的练习就是模拟这个过程。模拟成功之后就可以不用启动主体项目,前端用自己的小服务也可以开发查询应用页面了。
项目地址,欢迎纠错,提issue和点star,感谢。
功能包括:
开箱即用的意思就是,什么开发环境都没有的电脑也可以使用这套服务,省去了配置nodejs和配置oracle客户端的时间。
大家可以把这个当做一个简单的练习,用熟练了之后就可以在各位的项目中使用oracle了。
datasource.ini 中配置的是数据库连接信息,一行是一个库的信息,按照用户名、密码、连接串、别名来排序,中间用’,‘分隔。
install_oracle_ndoe里面放的是oracle客户端。
nodecode 里面放的是我们要写的server.js。
nodejs 里面放的nodejs安装后的文件夹。
getresult.js 是我封装的一个ajax调用后台接口的方法,接收参数是sql和数据库名称。
server.cmd 是写的批处理,用来动态设置系统环境变量(node和oracle客户端)和启动服务。
首先来看一下server.js
/*
* @Author: Dongge
* @Date: 2019-05-16 10:35:38
* @LastEditTime: 2019-05-24 09:56:04
* @Description: server for front-end,mock--getfpdjson V1.0
*/
// 经过和node-oracle 模块作者的讨论,合理配置了连接池参数,poolMax:连接池最大保持连接数,需要小于128,poolMin:连接池最小存活连接,poolPingInterval:探活时间,poolTimeout:连接被销毁时间,0为永远不销毁,针对个人开发测试,推荐永远不销毁。--20190520
// 同步和异步ajax测试均通过,模拟原始getresult接口的datasorce成功,增加数据库信息配置文件datasource.ini,增加读取配置文件功能 --20150523
// 编写批处理启动server.cmd,整合运行环境:nodejs包,oracle客户端包instantclient_11_2,发布版V1.0 --20150524
var http = require('http');
var oracledb = require("oracledb");
var fs = require("fs");
var readline = require("readline");
var path = require('path')
var querystring = require('querystring')
oracledb.poolMax = 100;
oracledb.poolMin = 1;
oracledb.poolPingInterval = 20;
oracledb.poolTimeout = 0;
process
.once('SIGTERM', closePoolAndExit)
.once('SIGINT', closePoolAndExit);
// readFile 读取文件函数,返回promise
var readFile = async function (path) {
var rl = readline.createInterface({
input: fs.createReadStream(path).setEncoding('utf8')
});
return new Promise((resolve, reject) => {
var array = [];
rl.on("line", line => {
array.push(line);
});
rl.on("close", () => {
resolve(array);
});
});
};
// 初始化连接池
var pool = {};
var initPool = async function () {
var dbconfig = await readFile('./datasource.ini');
for (var i = 0; i < dbconfig.length; i++) {
var temp = dbconfig[i].split(',');
try {
await oracledb.createPool({
_enableStats: true,
user: temp[0],
password: temp[1], // mypw contains the hr schema password
connectString: temp[2],
poolAlias: temp[3]
});
pool[temp[3]] = true;
} catch (err) {
pool[temp[3]] = false;
console.error(err.message);
throw new Error('连接池'+temp[3]+"创建失败!")
}
}
}
//退出执行函数
async function closePoolAndExit() {
console.log("\nTerminating");
try {
for(var key in pool){
await oracledb.getPool(key).close(10);
}
process.exit(0);
} catch (err) {
console.error(err.message);
process.exit(1);
}
}
//查询函数
var executeSql = async function (dataSource, sql) {
var pool_flag = pool[dataSource];
if (!pool_flag) {
return
}
var pool_name = oracledb.getPool(dataSource);
try {
var connection = await pool_name.getConnection();
let result = await connection.execute(sql);
await connection.close();
return Promise.resolve(result);
} catch (err) {
console.log(err.message)
}
};
(async () => {
await initPool();
//启动服务器
var server = http.createServer((req, res) => {
if (req.url === "/getResult") {
// 跨域,null是开启本地直接访问服务器特权
res.setHeader('Access-Control-Allow-Origin', 'null');
var params = '';
req.setEncoding('utf8');
// 采用post请求,接收前端的参数,这里考虑到sql里面可能有汉字,所以前端传过来的是编码后的JSON字符串。
req.on('data', function (chunk) {
params += chunk;
});
req.on('end', () => {
params = decodeURIComponent(unescape(params));
var { sql, dataSource } = JSON.parse(params);
(async () => {
try {
var { metaData, rows } = await executeSql(dataSource, sql);
var data = [];
for (var i = 0; i < rows.length; i++) {
var temp = {}
for (var j = 0; j < metaData.length; j++) {
temp[metaData[j]['name']] = rows[i][j];
}
data.push(temp);
}
var dataReturn = { flag: 1, data: data }
res.end(JSON.stringify(dataReturn));
} catch (err) {
res.end(JSON.stringify({ flag: 0, data: err.message }));
}
})();
})
} else if (req.url === "/getlog") {
//查看连接池状态信息接口,暂时写死了别名,调试用的,需要换成你自己的别名。
oracledb.getPool('J1_SGS')._logStats();
res.end();
}
})
await server.listen(8124)
})();
console.log('启动成功:监听端口8124')
接下来看一下getresult.js
/*
* @Author: Dongge
* @Date: 2019-05-24 10:13:55
* @LastEditTime: 2019-05-24 10:17:58
*/
var getResult = function (sql, dataSource) {
var result = {};
var params = {'sql':sql,'dataSource':dataSource};
params = escape(encodeURIComponent(JSON.stringify(params)));
$.ajax({
'url': 'http://127.0.0.1:8124/getResult',
'method': 'POST',
'data': params,
'async':false,
'dataType':"json",
'success': function (data) {
if(data.flag == 1){
result = data.data;
}else{
alert(data.err);
}
}, error: function (XMLHttpRequest, textStatus, errorThrown) {
alert('错误:' + XMLHttpRequest.status + '/' + XMLHttpRequest.readyState + "/" + textStatus)
}
});
return result;
}
最后看一下怎么调用
test.html
Document
然后让我们双击运行server.cmd,然后打开test.html网页,就可以看到结果被返回到了前台。
感谢 node-oracle github维护者cjbj和dmcghan的耐心讲解,解决了创建连接池如果用户名和密码输错了无法返回错误信息的问题(连接池创建的时候如果poolMin为0并不会建立连接,只是存了配置信息,所以不会得到错误返回值。)