0
点赞
收藏
分享

微信扫一扫

nodejs 使用连接池连接oracle 并构建查询服务

90哦吼 2021-09-23 阅读 92

nodejs启动一个简单的服务非常的迅捷,网上大部分数据库都是用的mysql,连接orcale的例子比较年代久远。基于oracle官方提供的node-oracle 模块来进行一次连接oracle,提供简单的查询服务。适用于传统it前端开发环境构建。

  • 前端开发环境的思考 :对于一些公司,系统及其庞大,前端工作可能只是庞大系统的某个业务模块的一小部分,原有的系统很难去进行改版,那么前端构建自己的小服务器,并且封装一些原系统名称同样的前端查询接口函数,在开发的过程中可以专注于前端的开发,开发结束之后在同一发布到原系统的测试环境进行测试,这样可以省去配置原系统耗费的时间;
  • 工程化 :这是一个很大的主题,前端的很多工程化目前都是借鉴的后台,需要明确的是,什么时候需要工程化,什么项目需要工程化,工程化并不是对所有的项目都适合,但是新的app和网站的开发推荐都使用新框架和框架带的工程化工具;
  • 本code适用和实现功能 :需要nodejs环境,还有oracle版本对应的客户端。实现创建连接池,查询,读取本地配置文件,环境整合。不是运行在服务器端提供一个服务,是每个人自己启动自己的服务,进行开发。

开发环境配置

首先确保安装成功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。

利用连接池连接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,然后给返回值,这个简单的练习就是模拟这个过程。模拟成功之后就可以不用启动主体项目,前端用自己的小服务也可以开发查询应用页面了。

github项目地址(各位可以直接下载使用,可能有点大,可以不下载oracle客户端11_2,换成你自己的,然后要改批处理里面的环境变量地址,有什么问题和想法提issue,动动小手给个星,万分感激。)

功能包括:

  • 提供本地可以访问的接口getResult
  • 数据库信息可以本地文件配置
  • 支持多数据库访问
  • 支持同步和异步ajax
  • 支持关闭服务同时关闭连接池
  • 支持纯净环境,开箱即用
  • 支持查看连接运行情况

开箱即用的意思就是,什么开发环境都没有的电脑也可以使用这套服务,省去了配置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();
//执行sql
    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

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src='jquery.min.js'></script>
    <script src='getresult.js'></script>
</head>

<body>

    <script>
        var a = getResult("select 123 from dual",'J1_SGS'); 
        var b = getResult("select 1 from dual",'J1_KJHS'); 

        console.log(a);
        console.log(b);
        
    </script>
</body>

</html>

然后让我们双击运行server.cmd,然后打开test.html网页,就可以看到结果被返回到了前台。

结束

感谢 node-oracle github维护者cjbj和dmcghan的耐心讲解,解决了创建连接池如果用户名和密码输错了无法返回错误信息的问题(连接池创建的时候如果poolMin为0并不会建立连接,只是存了配置信息,所以不会得到错误返回值。)

github项目地址(各位可以直接下载使用,可能有点大,可以不下载oracle客户端11_2,换成你自己的,然后要改批处理里面的环境变量地址,有什么问题和想法提issue,动动小手给个星,万分感激。)

举报

相关推荐

MySQL连接池

Java连接池

HikariCP连接池

Okhttp连接池

Jedis连接池

0 条评论