当前位置:网站首页>ORACLE 11G利用 ORDS+pljson来实现json_table 效果
ORACLE 11G利用 ORDS+pljson来实现json_table 效果
2022-07-02 06:24:00 【贤时间】
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 文档。
Declare
L_PO BLOB;
Begin
L_PO := :body;
INSERT INTO PurchaseOrder
SELECT * 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 PURCHASEORDER
add 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 LINEITEM
add primary key (PONO, ITEMNUMBER);
bloc_to_clob转换函数
CREATE OR REPLACE FUNCTION blob_to_clob(blob_in IN BLOB) RETURN CLOB AS
v_clob CLOB;
v_varchar VARCHAR2(32767);
v_start PLS_INTEGER := 1;
v_buffer PLS_INTEGER := 32767;
BEGIN
DBMS_LOB.CREATETEMPORARY(v_clob, TRUE);
FOR i IN 1 .. CEIL(DBMS_LOB.GETLENGTH(blob_in) / v_buffer) LOOP
v_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_id
FROM 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_id
FROM 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 IS
PROCEDURE 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)
IS
c_body CLOB := blob_to_clob(p_body);
l_request_id NUMBER := cux_apex_json_clob_data_s.nextval;
BEGIN
INSERT INTO cux_apex_json_clob_data
VALUES
(l_request_id, c_body, SYSDATE);
plog.debug(l_request_id || '-insert cux_apex_json_clob_data rows:' || SQL%ROWCOUNT);
INSERT INTO PurchaseOrder
SELECT *
FROM demo_header_v
WHERE request_id = l_request_id;
plog.debug(l_request_id || '-insert PurchaseOrder rows:' || SQL%ROWCOUNT);
INSERT INTO LineItem
SELECT *
FROM demo_line_v
WHERE request_id = l_request_id;
plog.debug(l_request_id || '-insert LineItem rows:' || SQL%ROWCOUNT);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
plog.full_error_backtrace;
RAISE;
END create_order;
BEGIN
NULL;
END cux_apex_json_demo_pkg;
/
封装到ORDS中
BEGIN
ORDS.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 =>
'BEGIN cux_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.takeDate from 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
边栏推荐
- SQLI-LABS通关(less6-less14)
- Common prototype methods of JS array
- Utilisation de la carte et de foreach dans JS
- MySQL index
- Date time API details
- Browser scrolling for more implementations
- Laravel8中的find_in_set、upsert的使用方法
- ModuleNotFoundError: No module named ‘jieba. analyse‘; ‘ jieba‘ is not a package
- PM2 simple use and daemon
- mapreduce概念和案例(尚硅谷学习笔记)
猜你喜欢
SQLI-LABS通關(less6-less14)
Take you to master the formatter of visual studio code
Sqli-labs customs clearance (less6-less14)
Review of reflection topics
Spark的原理解析
Cloud picture says | distributed transaction management DTM: the little helper behind "buy buy buy"
Ingress Controller 0.47.0的Yaml文件
[Zhang San learns C language] - deeply understand data storage
In depth study of JVM bottom layer (V): class loading mechanism
PHP Session原理简析
随机推荐
Cve-2015-1635 (ms15-034) Remote Code Execution Vulnerability recurrence
The win10 network icon disappears, and the network icon turns gray. Open the network and set the flash back to solve the problem
CSRF攻击
DNS攻击详解
工具种草福利帖
In depth study of JVM bottom layer (3): garbage collector and memory allocation strategy
CTF three count
JS create a custom JSON array
Browser scrolling for more implementations
php中树形结构转数组(拉平树结构,保留上下级排序)
ORACLE EBS接口开发-json格式数据快捷生成
How to try catch statements that return promise objects in JS
Kali latest update Guide
解决微信小程序swiper组件bindchange事件抖动问题
Sentry construction and use
ORACLE APEX 21.2安装及一键部署
JS divides an array into groups of three
Flask migrate cannot detect db String() equal length change
Deployment API_ automation_ Problems encountered during test
ts和js区别