Oracle 在12.1中引入了对json的支持,可以利用sql来查询json字段各个节点的值,对于11G的版本,例如EBS环境,不方便升级到12C,可以利用开源软件pljson 来实现,本文来利用ORDS + pljson 来实现开发一个post示例请求,接收订单数据。

涉及软件组件

ORDS 20.2
pljson 3.5.2
Oracle dbms 11GR2
Oracle apex 20.1

json数据源实例

下面这一段就是要处理的json,是多层次的机构,包含订单头信息和两个订单行

{"PONumber": 1608,"Requestor": "Alexis Bull","CostCenter": "A50","Address": {"street": "200 Sporting Green","city": "South San Francisco","state": "CA","zipCode": 99236,"country": "United States of America"},"LineItems": [{"ItemNumber": 1,"Part": {"Description": "One Magic Christmas","UnitPrice": 19.95,"UPCCode": 1313109289},"Quantity": 9.0},{"ItemNumber": 2,"Part": {"Description": "Lethal Weapon","UnitPrice": 19.95,"UPCCode": 8539162892},"Quantity": 5.0}]
}

数据来源参考文档

技术实现

在上面的Oracle官方文档中,使用了json_table函数,这个函数和11G的xmltable函数语法风格很像,这个是12C之后才有的一个新功能,对于处理json字符串十分方便,但是对于12.1之前的版本没这个功能,Oracle总是慢一步,在12C之前大家对json需求比较强烈,于是就有了开源工具pljson,这个插件本质上对Oracle集合的一些运用。
但是开源的不如原生的从底层实现的好用,比如有个查询功能如下面这段代码:

select *
from table(
pljson_table.json_table(
'{"data":{"name": "name 3","description": "blah blah...","type": "text","created_time": "2015-12-21T19:23:29+0000","shares": { "count": 100 },"extra": null,"maps" : [ true, true, false ]}
}',
pljson_varray('data.name', 'data.extra', 'data.maps', 'data.shares.count', 'data.missing'),
pljson_varray('name', 'extra', 'map', 'count', 'whatelse'))
)

作为一个sql脚本在sql引擎中运行时没问题的,但是在pl/sql引擎下是会报错 ORA-22905 无法从非嵌套表项访问行,本质是用table的时候需要在pl/sql块中声明下table里面的是个啥类型。但是pljson_table.json_table的返回值是一个anydataset,这个Oracle ANYDATASET type又有一套玩法,暂时不去研究怎么实现在pl/sql里面用这个了。转变下思路,我们可以在上面这个查询封装到一个视图里面,视图是用的sql引擎,然后在pl/sql里面对这个视图进行查询就好了。

官方代码

我们本质上是要实现如下效果,下面代码来自Oracle官方ORDS 文档。

DeclareL_PO     BLOB;BeginL_PO := :body;INSERT INTO PurchaseOrderSELECT * FROM json_table(L_PO  FORMAT JSON, '$'COLUMNS (PONo            Number    PATH '$.PONumber',Requestor       VARCHAR2  PATH '$.Requestor',CostCenter      VARCHAR2  PATH '$.CostCenter',AddressStreet   VARCHAR2  PATH '$.Address.street',AddressCity     VARCHAR2  PATH '$.Address.city',AddressState    VARCHAR2  PATH '$.Address.state',AddressZip      VARCHAR2  PATH '$.Address.zipCode',AddressCountry  VARCHAR2  PATH '$.Address.country'));INSERT INTO LineItem
SELECT * FROM json_table(L_PO  FORMAT JSON, '$'COLUMNS (PONo  Number PATH '$.PONumber',NESTED             PATH '$.LineItems[*]'COLUMNS (ItemNumber        Number   PATH '$.ItemNumber',PartDescription   VARCHAR2   PATH '$.Part.Description',PartUnitPrice     Number   PATH '$.Part.UnitPrice',PartUPCCode       Number   PATH '$.Part.UPCCode',Quantity          Number   PATH '$.Quantity')));
commit;
end;

json_table(L_PO FORMAT JSON 这个功能很强大,一下子实现了很多东西,blob 到clob的转换,集合的定义。

为此我们需要自己实现blob到clob转换,因为pljson接收的入参必须是clob类型。

表结构

表结构如下:

--接收POST过来原始数据的表
-- Create table
create table CUX_APEX_JSON_CLOB_DATA
(request_id    NUMBER not null,json_lob      CLOB,creation_date DATE default SYSDATE
);
-- Create/Recreate indexes
create unique index CUX_APEX_JSON_CLOB_DATA_U1 on CUX_APEX_JSON_CLOB_DATA (REQUEST_ID);
--序列
create sequence CUX_APEX_JSON_CLOB_DATA_S;-- Create table 订单头表
create table PURCHASEORDER
(pono           NUMBER(5) not null,requestor      VARCHAR2(50),costcenter     VARCHAR2(5),addressstreet  VARCHAR2(50),addresscity    VARCHAR2(50),addressstate   VARCHAR2(2),addresszip     VARCHAR2(10),addresscountry VARCHAR2(50),request_id     NUMBER not null
);
-- Create/Recreate primary, unique and foreign key constraints
alter table PURCHASEORDERadd primary key (PONO);-- Create table 订单行表
create table LINEITEM
(pono            NUMBER(5) not null,itemnumber      NUMBER(10) not null,partdescription VARCHAR2(50),partunitprice   NUMBER(10),partupccode     NUMBER(10),quantity        NUMBER(10),qequest_id      NUMBER not null
);
-- Create/Recreate primary, unique and foreign key constraints
alter table LINEITEMadd primary key (PONO, ITEMNUMBER);

bloc_to_clob转换函数

CREATE OR REPLACE FUNCTION blob_to_clob(blob_in IN BLOB) RETURN CLOB ASv_clob    CLOB;v_varchar VARCHAR2(32767);v_start   PLS_INTEGER := 1;v_buffer  PLS_INTEGER := 32767;
BEGINDBMS_LOB.CREATETEMPORARY(v_clob, TRUE);FOR i IN 1 .. CEIL(DBMS_LOB.GETLENGTH(blob_in) / v_buffer) LOOPv_varchar := UTL_RAW.CAST_TO_VARCHAR2(DBMS_LOB.SUBSTR(blob_in,v_buffer,v_start));DBMS_LOB.WRITEAPPEND(v_clob, LENGTH(v_varchar), v_varchar);--DBMS_OUTPUT.PUT_LINE(v_varchar);v_start := v_start + v_buffer;END LOOP;RETURN v_clob;
END blob_to_clob;

视图

--------------------需要注意,视图select字段必须大写----------------------------------
--头表视图
CREATE OR REPLACE VIEW DEMO_HEADER_V AS
SELECT js."PONO",js."REQUESTOR",js."COSTCENTER",js."ADDRESSSTREET",js."ADDRESSCITY",js."ADDRESSSTATE",js."ADDRESSZIP",js."ADDRESSCOUNTRY",tab.request_idFROM cux_apex_json_clob_data tab,TABLE(pljson_table.json_table(tab.json_lob,pljson_varray('PONumber','Requestor','CostCenter','Address.street','Address.city','Address.state','Address.zipCode','Address.country'),pljson_varray('pono','requestor','costcenter','addressstreet','addresscity','addressstate','addresszip','addresscountry'))) js;--行表视图
CREATE OR REPLACE VIEW DEMO_LINE_V AS
SELECT js."PONO",js."ITEMNUMBER",js."PARTDESCRIPTION",js."PARTUNITPRICE",js."PARTUPCCODE",js."QUANTITY",pljtt.request_idFROM cux_apex_json_clob_data pljtt,TABLE(pljson_table.json_table(pljtt.json_lob,pljson_varray('PONumber','LineItems[*].ItemNumber','LineItems[*].Part.Description','LineItems[*].Part.UnitPrice','LineItems[*].Part.UPCCode','LineItems[*].Quantity'),pljson_varray('pono','itemnumber','partdescription','partunitprice','partupccode','quantity'),table_mode => 'nested')) js;

pl/sql 代码

CREATE OR REPLACE PACKAGE cux_apex_json_demo_pkg ISPROCEDURE create_order(p_body IN BLOB);END cux_apex_json_demo_pkg;
/
CREATE OR REPLACE PACKAGE BODY cux_apex_json_demo_pkg IS--******************************************************************************-- FUNCTION get_tax_rate----   p_body    POST请求过来的BLOB数据----   Public. demo演示,获取POST过来的json数据,将其入库----******************************************************************************PROCEDURE create_order(p_body IN BLOB)ISc_body       CLOB := blob_to_clob(p_body);l_request_id NUMBER := cux_apex_json_clob_data_s.nextval;BEGININSERT INTO cux_apex_json_clob_dataVALUES(l_request_id, c_body, SYSDATE);plog.debug(l_request_id || '-insert cux_apex_json_clob_data rows:' || SQL%ROWCOUNT);INSERT INTO PurchaseOrderSELECT *FROM demo_header_vWHERE request_id = l_request_id;plog.debug(l_request_id || '-insert PurchaseOrder rows:' || SQL%ROWCOUNT);INSERT INTO LineItemSELECT *FROM demo_line_vWHERE request_id = l_request_id;plog.debug(l_request_id || '-insert LineItem rows:' || SQL%ROWCOUNT);COMMIT;EXCEPTIONWHEN OTHERS THENplog.full_error_backtrace;RAISE;END create_order;BEGINNULL;
END cux_apex_json_demo_pkg;
/

封装到ORDS中

BEGINORDS.ENABLE_SCHEMA(p_enabled             => TRUE,p_schema              => 'CUX_APEX',p_url_mapping_type    => 'BASE_PATH',p_url_mapping_pattern => 'CUX_APEX',p_auto_rest_auth      => FALSE);ORDS.DEFINE_MODULE(p_module_name    => 'demo',p_base_path      => '/demo/',p_items_per_page => 25,p_status         => 'PUBLISHED',p_comments       => 'json多行测试');ORDS.DEFINE_TEMPLATE(p_module_name    => 'demo',p_pattern        => 'test',p_priority       => 0,p_etag_type      => 'HASH',p_etag_query     => NULL,p_comments       => NULL);ORDS.DEFINE_HANDLER(p_module_name    => 'demo',p_pattern        => 'test',p_method         => 'POST',p_source_type    => 'plsql/block',p_items_per_page => 0,p_mimes_allowed  => '',p_comments       => NULL,p_source         =>
'BEGINcux_apex_json_demo_pkg.create_order(:BODY);
END;
');COMMIT;END;

在aepx中的定义是如下效果:

附加更新

上面的例子需要将json数据入库,其实用动态sql的话,可以将json作为一个绑定变量传入,这样就避免了插入冗余数据了,但是会丢失原始传过来的json数据。

--json内容{"DATA":
[{"storeId":"1","supplierId":"111","takeCost":"1.00","takeDate":"20191112"},
{"storeId":"2","supplierId":"2","takeCost":"200.00","takeDate":"20191114"},
{"storeId":"2","supplierId":"3","takeCost":"100.00","takeDate":"20191111"}]
}--动态sql
execute immediate q'{ insert into supp_cost_t(storeid,supplierid,takecost,takedate)select b.storeid, b.supplierId, b.takeCost, b.takeDatefrom table(pljson_table.json_table(:1,pljson_varray('DATA[*].storeId','DATA[*].supplierId','DATA[*].takeCost','DATA[*].takeDate'),pljson_varray('storeId','supplierId','takeCost','takeDate'),table_mode => 'nested')) b}'using json_CLOB;

参考博客:
https://darkathena.blog.csdn.net/article/details/120644329?spm=1001.2014.3001.5502

ORACLE 11G利用 ORDS+pljson来实现json_table 效果相关推荐

  1. Oracle备份standby,Oracle 11g 利用泠备份恢复standby库

    Oracle 11g 利用泠备份恢复standby库 1 开始在备库上进行泠备份 先查好控制文件.redo.undo文件.数据文件的路径 1.1 先关闭主库的归档日志传输 SQL> ALTER ...

  2. 利用sqoop将oracle 11g中的表迁移至hive表

    [Prerequisite] hadoop, hive, sqoop installed ojdbc6.jar was in directory /usr/lib/sqoop/lib (ojdbc6. ...

  3. oracle utl_smtp,Oracle 11g 环境下,利用utl_smtp创建发送邮件的存储过程

    网上太多发邮件储存过程,我就不转发了,弄个简单的作为示例: create or replace procedure Send_mail(mail_body varchar2) is smtp_conn ...

  4. Oracle 11G for redhat 自启动脚本

    Oracle 11G for redhat 自启动脚本 在$ORACLE_HOME/bin中,有dbstart和dbshut这两个脚本,可以使用这两个oracle自带的脚本实现oracle的开机自启动 ...

  5. Oracle 11g AMM与ASMM切换

    现在的Oracle正在往智能化方向发展.如果我们现在找一些8i/9i时代的Oracle书籍,怎么样配置合适的数据库各内存池大小是非常重要的话题.但是进入10g之后,自动内存池调节成为一个重要Oracl ...

  6. Oracle 11g数据库的部署

    一.前言 最近要准备考试了,准备温习下Oracle数据库的相关知识,那么学习Oracle的前提肯定是部署了,目前主流Oracle版本为11g,那么接下来我们就先来一起看下,如何部署Oracle已经对数 ...

  7. Oracle 11g 新特性简介

    Oracle 11g于2007年7月11日美国东部时间11时(北京时间11日22时)正式发布,11g是甲骨文公司30年来发布的最重要的数据库版本,根据用户的需求实现了信息生命周期管理(Informat ...

  8. Oracle 11g RAC features

    <一,> oracle 11g r2 RAC提供了以下功能: 高可用:shared-everything 模式保证了单节点的故障不会停止服务,集群中的其他节点将快速接管 可扩展性:多节点分 ...

  9. oracle查询100到200数据,100分数据库查询语句(ORACLE 11g)

    100分求一个数据库查询语句(ORACLE 11g) 一张表tabl1 如下: epqname             createtime                             e ...

  10. t oracle删除吗,Oracle 11g 手工建库与删库

    Oracle 11g 手工建库与删库 在前一篇文章中提到阅读Oracle 11gR2 Administrator's Guide(文档编号E25494-01)时,简单描述了DDL_LOCK_TIMEO ...

最新文章

  1. pandas使用groupby.last函数获取每个组中的最后一个值实战:groupby.last函数获取每个组中的最后一个值、groupby.nth函数获取每个组中的最后一个值
  2. 人脸识别之insightface开源代码使用:训练、验证、测试(4)
  3. 深度学习小技巧(二):如何保存和恢复scikit-learn训练的模型
  4. 取消挂载点可以节省磁盘么_Linux下NTFS格式外接硬盘的挂载,取消挂载和开机自动挂载...
  5. springboot 系列教程十:springboot单元测试
  6. dom对象常用的属性和方法有哪些?
  7. 来自百度的一篇如何关闭Centos7的防火墙以及如何永久关闭防火墙的指令教学【转载】
  8. 微信商户现金红包api php
  9. linux蚂蚁矿池教程,蚂蚁矿机Z9使用教程
  10. HTML+CSS小白入门与进阶教程
  11. OCP 11G 053题库解析汇总链接(1-200)
  12. 模糊特征隶属度及图像增强相关
  13. 阿里云ECS开放指定端口步骤
  14. 算力智库2021隐私计算论坛圆满落幕,隐私计算落地会长出怎样的新商业模式?
  15. Tableau 5阶桑基图
  16. 锁模块之当前读和快照读
  17. 软件测试之自动化测试Pytest教程
  18. html如何制作一个漂亮的表格?+ 列表制作 + 表单制作(干货!直接收代码)
  19. 亚马逊A9算法是什么?有什么作用?(案例详解)
  20. Windows远程桌面(xrdp+vnc)连接Ubu…

热门文章

  1. python函数长度单位换算,(最新整理)长度单位换算
  2. 2020SpringCloud学习年终总结——第九章-Gateway
  3. 【图像分类数据集】非常全面实用的垃圾分类图片数据集共享
  4. c语言for语句10内的累乘,c语言累加累乘教案.doc
  5. linux下ftp工具
  6. 如何设置微信群名称?
  7. 游戏开发入门-现在学Unity晚了么?Unity的前景如何?
  8. hdu 5455 Fang Fang 2015沈阳网络赛 模拟
  9. 聚集索引,非聚集索引,覆盖索引 原理
  10. Spark课程设计——电影推荐系统