ORACLE 11G uses ORDS+pljson to achieve json_table effect

Oracle introduced support for json in 12.1, you can use sql to query the value of each node in the json field, for the 11G version, such as the EBS environment, it is not convenient to upgrade to 12C, you can use the open source software pljson to achieve, this article uses ORDS + pljson to implement the development of a post example request to receive order data.

involving software components

ORDS 20.2
pljson 3.5.2
Oracle dbms 11GR2
Oracle apex 20.1

json data source instance

The following paragraph is the json to be processed, which is a multi-level organization, including order header information and two order lines

{
    
    
	"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
		}
	]
}

Data Source Reference Documentation

Technical realization

In the official Oracle document above, the json_table function is used. This function is similar in syntax to the xmltable function of 11G. This is a new function only available after 12C. It is very convenient for processing json strings, but for versions before 12.1 Without this function, Oracle is always a step behind. Before 12C, everyone had a strong demand for json, so there was an open source tool pljson. This plug-in essentially uses some Oracle collections.
However, open source is not as easy to use as the native implementation from the bottom layer. For example, there is a query function like the following code:

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'))
)

As a sql script, there is no problem when running in the sql engine, but under the pl/sql engine, an error ORA-22905 cannot be accessed from a non-nested table item. The essence is that when using a table, it needs to be in the pl/sql block. Declare what type is in the table. However, the return value of pljson_table.json_table is an anydataset, and this Oracle ANYDATASET type has another way of playing, so I will not study how to use this in pl/sql for the time being. To change the way of thinking, we can encapsulate the above query into a view, which uses the sql engine, and then query the view in pl/sql.

official code

We essentially want to achieve the following effects, the following code comes from Oracle's official ORDS documentation.

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 This function is very powerful, and it realizes many things at once, such as the conversion from blob to clob, and the definition of collection.

For this reason, we need to implement blob to clob conversion by ourselves, because the input parameter received by pljson must be of clob type.

Table Structure

The table structure is as follows:

--接收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 conversion function

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;

view

--------------------需要注意,视图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 code

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;
/

Encapsulated into 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;

The definition in aepx is as follows:

insert image description here

additional update

The above example needs to put json data into the database. In fact, if dynamic sql is used, json can be passed in as a binding variable, so as to avoid inserting redundant data, but the original json data passed in will be lost.

--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;

Reference blog:
https://darkathena.blog.csdn.net/article/details/120644329?spm=1001.2014.3001.5502

Guess you like

Origin blog.csdn.net/x6_9x/article/details/119024574