异构数据源的数据查询功能
在上一篇文章中我们通过API平台定义了一个最基本的数据查询接口,本篇文章我们将上升难度,在原有接口的基础上,实现在MySQL数据库和Oracle数据库同时进行数据查询。
什么场景会需要同时对异构数据源进行查询?接着上篇文章的例子,比如您有一个老的业务系统是基于Oracle数据库开发的,后来系统升级,新业务系统是基于MySQL数据库开发的,由于种种原因,老系统的数据尚未迁移到新系统(或者过渡阶段新老系统可能还在同步运行,例如住建局的网签备案系统,老楼盘可能继续在老系统签订合同,新楼盘则在新系统签订合同),但我们又不得不确保另一个公司开发的预售资金监管系统能通过咱们的一个API接口查询到所有的买卖合同信息。下面来看看我们的API平台是如何轻松实现这一需求的。
1.测试数据准备
上篇文章我们已经准备了一个MySQL测试库business_system,我们把它当成新网签备案系统的数据库,那么这里我们再准备一个Oracle的测试库business_system_old来当成老网签备案系统的数据库,其中包括与business_system类似的三张表(为了区分,我这里故意把表名称和表结构设计得不一样):
2.1 幢表(db_buildings)
表结构如下:
幢表准备了两条数据:
2.2 户表(db_houses)
户表通过"building_id"外键字段与db_buildings表关联,表明这套房子属于哪个楼幢,表结构如下:
户表准备了两条数据:
2.3 交易者表(db_contracts)
交易者表记录了谁购买了哪一套房子(通过户house_id号关联户表),以及买卖合同编号等信息,表结构如下:
交易者表中保存了两个合同,其中的CL20231101000001合同有两个卖方(卖方A、卖方B)、两个买方(买方C、买方D);CL20231101000002合同有一个卖方(卖方E)和一个买方(买方F),数据如下:
2.4 接口需求
让预售资金监管系统以买卖合同号作为入参,使用GET请求查询该合同在网签备案系统对应的楼栋基本信息、户基本信息、卖方基本信息、买方基本信息,并且同时查询新老系统,要求接口响应如下json格式的业务数据:
{
"ywbh": "业务编号",
"htbh": "合同编号",
"building": {
"bdcdyh": "幢登记单元代码/不动产单元号",
"zl": "幢坐落"
},
"houses": [{
"bdcdyh": "户登记单元代码/不动产单元号",
"fwbm": "房屋编码",
"szqsc": "所在起始层",
"szzzc": "所在终止层",
"shbw": "室号部位",
"fwzl": "房屋坐落",
"jzjg": "建筑结构",
"jzjgbm": "建筑结构编码",
"fwyt": "房屋用途",
"fwytbm": "房屋用途编码",
"jzmj": "建筑面积"
}],
"sellers": [{
"jyzqc": "交易者全称",
"jyzzjmc": "交易者证件名称",
"jyzzjhm": "交易者证件号码",
"jyzxz": "交易者性质"
}],
"buyers": [{
"jyzqc": "交易者全称",
"jyzzjmc": "交易者证件名称",
"jyzzjhm": "交易者证件号码",
"jyzxz": "交易者性质"
}]
}
其中building用来装载幢基本信息,houses用来装载户基本信息,sellers用来装载卖方信息,buyers用来装载买方信息。
3.开始定义接口
3.1 新增OAuth客户端
我们把老网签备案系统作为客户端添加到API平台中,点击主界面的[OAuth客户端]菜单->点击[新增],填写如下字段:
点击保存即可,字段含义在上篇文章解释过,这里不再重复说明。
接下来,我们只需要在上篇文章定义的接口的基础上新增一个分享(即新增一个执行策略)即可。
3.2 新增一个分享
先在路由设置列表页面找到我们之前定义的路由,然后点击路由的[分享设置]-[新增],填写以下字段:
字段含义在上篇文章介绍过了,这里不再赘述。
注意分享编码不要和之前那个分享设置的编码重复(同一个路由下不同的分享设置必须要有不同的分享编码),分享名称根据实际情况填写即可;然后填写咱们Oracle数据库的连接信息,其它字段暂时用不上依然使用默认值,填写完成后点击保存即可。保存后,您将看到以下界面:
先选中刚才添加的分享设置,点击"数据库连接测试",看看我们填写的Oracle数据库连接信息是否正确,如图:
如果数据库连接成功,那么我们就可以在这个分享设置下编写sql语句了。
3.3 编写sql组装业务数据
点击分享设置的[数据定义],如图:
sql定义的过程可以完全参照上篇文章的过程,针对Oracle实际的库结构编写sql语句即可。
首先,我们把json报文的根节点字段查询出来,sql如下:
select distinct ywbh,contract_number as htbh from db_contracts j where j.contract_number = request~htbh~
如图:
接下来我们组装json中building(幢)节点的数据。点击点击分享设置的[数据定义]->[新增]
填写如下sql语句:
SELECT DISTINCT z.bdcdyh, z.location as zl
FROM db_buildings z
JOIN db_houses h ON h.building_id = z.id
JOIN db_contracts jyz ON h.id = jyz.house_id
AND jyz.ywbh = parent~ywbh~
如图:
接下来我们组装json中houses(户)节点的数据。点击点击分享设置的[数据定义]->[新增]
填写如下sql语句:
SELECT DISTINCT
h.bdcdyh,
h.fwbm,
h.start_floor AS szqsc,
h.end_floor AS szzzc,
h.house_number AS shbw,
h.location AS fwzl,
h.structure AS jzjg,
h.plan_use AS fwyt,
h.area
FROM
db_houses h
JOIN db_contracts jyz ON h.id = jyz.house_id
AND jyz.ywbh = parent~ywbh~
如图:
接下来我们组装json中sellers(卖方)节点的数据。点击点击分享设置的[数据定义]->[新增]
填写如下sql语句:
SELECT
j.trader_name AS jyzqc,
j.trader_zjmc AS jyzzjmc,
j.trader_zjhm AS jyzzjhm,
j.trader_xz AS jyzxz
FROM
db_contracts j
WHERE
j.ywbh = parent~ywbh~
AND j.trader_lb = '卖方'
如图:
接下来我们组装json中buyers(买方)节点的数据。点击点击分享设置的[数据定义]->[新增]
填写如下sql语句:
SELECT
j.trader_name AS jyzqc,
j.trader_zjmc AS jyzzjmc,
j.trader_zjhm AS jyzzjhm,
j.trader_xz AS jyzxz
FROM
db_contracts j
WHERE
j.ywbh = parent~ywbh~
AND j.trader_lb = '买方'
如图:
到此为止,我们所有的sql都添加好了,数据定义列表截图如下:
4.在线测试接口
回到路由设置的列表页,找到我们刚才的路由,点击[打开文档],如图所示:
点击后可以看到API平台为我们自动生成的在线文档,如图:
我们先填写入参值:YS20231203000001(新网签备案系统的合同编号),点击[试一下],接口调用该接口,如图:
可以看到API平台为这个接口自动生成的接口地址与上篇文章相比没有任何变化,依然是"http://127.0.0.1:8080/adi/bitapi/queryMmhtJbxx",其中的adi和bitapi是固定路径,queryMmhtJbxx则是我们自定义的路由编码;响应报文如下:
{
"type": "success",
"data": {
"multiThreadResponses": [
{
"ywbh": "20231203000001",
"htbh": "YS20231203000001",
"building": {
"bdcdyh": "110108001001GB00001F0001",
"zl": "北京市海淀区曙光中路曙光花园智业园1幢"
},
"houses": [
{
"bdcdyh": "110108001001GB00001F00010003",
"fwbm": "fbm0003",
"szqsc": 1,
"szzzc": 1,
"shbw": "103",
"fwzl": "北京市海淀区曙光中路曙光花园智业园1幢B座1-103",
"jzjg": "钢结构",
"fwyt": "住宅",
"jzmj": 183
}
],
"sellers": [
{
"jyzqc": "张三",
"jyzzjmc": "居民身份证",
"jyzzjhm": "510121100909137101",
"jyzxz": "外省市个人"
},
{
"jyzqc": "李四",
"jyzzjmc": "军官证",
"jyzzjhm": "wj0001",
"jyzxz": "军人"
}
],
"buyers": [
{
"jyzqc": "王五",
"jyzzjmc": "户口簿",
"jyzzjhm": "522121100909137102",
"jyzxz": "本市城镇居民"
},
{
"jyzqc": "赵六",
"jyzzjmc": "港澳台身份证",
"jyzzjhm": "gat0001",
"jyzxz": "台湾同胞"
}
]
},
{}
]
},
"uuid": "4302af,18db3e"
}
其中的type、data、uuid依然是API平台响应的固定参数,观察报文,会发现和上篇文章只有一个分享设置时的响应报文有所区别:
首先,业务数据最外层多了一个multiThreadResponses节点,数据类型是jsonArray,它表示多线程响应,这是因为我们这个路由下有多个分享设置,且这些分享设置被多线程并行执行了,multiThreadResponses装载了所有分享设置响应的业务数据,这个数组的第二个元素是个空对象({}),表示其中一个分享设置没有查到任何数据。
其次,uuid变成了两个值(4302af,18db3e),这两个值各自对应了一个分享设置的请求唯一编码(只截取了前6位)。
让我们查询最新的分享日志看看,如图:
通过分享日志可以看到,两个分享设置都被成功执行了。
接下来,我们在入参输入一个老系统的合同编号CL20231101000001,并点击试一下,如图:
响应报文如下:
{
"type": "success",
"data": {
"multiThreadResponses": [
{},
{
"ywbh": "20231101000001",
"htbh": "CL20231101000001",
"building": {
"bdcdyh": "110108001001GB00001F0003",
"zl": "北京市海淀区曙光中路曙光花园智业园3幢"
},
"houses": [
{
"bdcdyh": "110108001001GB00001F00030001",
"fwbm": "fbmF00030001",
"szqsc": 1,
"szzzc": 1,
"shbw": "101",
"fwzl": "北京市海淀区曙光中路曙光花园智业园3幢B座1-101",
"jzjg": "钢结构",
"fwyt": "住宅",
"area": 100
}
],
"sellers": [
{
"jyzqc": "卖方A",
"jyzzjmc": "居民身份证",
"jyzzjhm": "510121100909137105",
"jyzxz": "本市城镇居民"
},
{
"jyzqc": "卖方B",
"jyzzjmc": "居民身份证",
"jyzzjhm": "510121100909137106",
"jyzxz": "本市城镇居民"
}
],
"buyers": [
{
"jyzqc": "买方C",
"jyzzjmc": "居民身份证",
"jyzzjhm": "510121100909137107",
"jyzxz": "本市城镇居民"
},
{
"jyzqc": "买方D",
"jyzzjmc": "居民身份证",
"jyzzjhm": "510121100909137108",
"jyzxz": "本市城镇居民"
}
]
}
]
},
"uuid": "ac8553,fd8391"
}
可以看到,老系统的合同信息也被查询出来了,新系统的查询结果是空对象({})。
到这里,同时查询新老系统的需求已经实现了,但您可能会有疑问:多个分享设置的时候,响应报文的格式和之前发生了变化,会增加接口调用者解析报文的难度,这该怎么办呢?当然有办法处理,继续往下看。
4.调整相应报文的格式-ADI响应_转换JS
打开路由设置的响应,会看到一个字段:ADI响应_转换JS,如图:
在这个字段中,我们可以编写一个JavaScript方法(里面还可以有java语法),实现对data节点报文格式的任意转换:
function formatterJson(oldJson,newJson){
if(oldJson.multiThreadResponses) { //如果是多线程返回结果
for (var i = 0; i <oldJson.multiThreadResponses.length; i++){ //变量多线程结果
if(oldJson.multiThreadResponses[i].size() > 0) { //如果有结果,即不是空对象{}
newJson = oldJson.multiThreadResponses[i]; //把结果赋值给newJson
break;
}
}
return newJson; //有multiThreadResponses,则返回newJson
}
return oldJson; //没有multiThreadResponses节点,可能只启用了一个分享设置,就直接返回
}
注意: 方法名formatterJson是固定写法,不能变;oldJson是格式转换前data节点下的原始json数据;newJson是空的json对象(即{}),一般用来接收格式转换后的json数据。
如图:
点击保存即可。
接下来,我们再调用接口,看看响应报文:
{
"type": "success",
"data": {
"ywbh": "20231101000001",
"htbh": "CL20231101000001",
"building": {
"bdcdyh": "110108001001GB00001F0003",
"zl": "北京市海淀区曙光中路曙光花园智业园3幢"
},
"houses": [
{
"bdcdyh": "110108001001GB00001F00030001",
"fwbm": "fbmF00030001",
"szqsc": 1,
"szzzc": 1,
"shbw": "101",
"fwzl": "北京市海淀区曙光中路曙光花园智业园3幢B座1-101",
"jzjg": "钢结构",
"fwyt": "住宅",
"area": 100
}
],
"sellers": [
{
"jyzqc": "卖方A",
"jyzzjmc": "居民身份证",
"jyzzjhm": "510121100909137105",
"jyzxz": "本市城镇居民"
},
{
"jyzqc": "卖方B",
"jyzzjmc": "居民身份证",
"jyzzjhm": "510121100909137106",
"jyzxz": "本市城镇居民"
}
],
"buyers": [
{
"jyzqc": "买方C",
"jyzzjmc": "居民身份证",
"jyzzjhm": "510121100909137107",
"jyzxz": "本市城镇居民"
},
{
"jyzqc": "买方D",
"jyzzjmc": "居民身份证",
"jyzzjhm": "510121100909137108",
"jyzxz": "本市城镇居民"
}
]
},
"uuid": "d84de0,b0d3a1"
}
响应的业务数据格式就和之前一样了,问题被解决。
API平台的js执行能力使得平台功能更加灵活,有时候我们可能想合并多个分享设置的执行结果(比如要查询某个人名下的房产,可能需要调用各区县的业务系统得到查询结果),通过js格式转换也是很容易实现的。
到这里,相信大家已经知道如何同时对多个数据源进行查询了,后续我将继续介绍ADI平台其它的重要功能,谢谢您的阅读!