Oracle Apex - przechowywanie danych w tabeli Oracle w formacie JSON
Jeśli wykorzystujemy tabele bazy danych do przechowywania ustawień aplikacji, często dochodzimy do momentu kiedy ilość pól jest niewystarczająca i musimy dodać kolejne pole. A z drugiej strony mamy parametry które nie potrzebują takiej ilości pól. Mamy też sytuacje kiedy nigdy nie wiemy ile pól będziemy potrzebować dla jakiegoś parametru. Rozwiązaniem tego formatu jest JSON. W jednym polu możemy przechować praktycznie każdą strukturę, każde ustawienia.
Tworzenie w tabeli Oracle pola typu JSON i używanie JSON w celu przechowania informacji.
Oracle nie posiada gotowego pola typu JSON. Takim formatem może być VARCHAR2 (limit znaków) CLOB lub BLOB jeśli przechowujesz dane binarne i nie chcesz niepożądanej konwersji znaków. Tabelę możesz utworzyć używając SQL Developer, możesz użyć też opcji SQL Workshop w Oracle Apex i wklejając odpowiednie komendy SQL. Jednak Oracle Apex oferuje wygodne GUI ktore przeprowadzi Cie przez cały proces. Więcej, nie musisz zawracać sobie głowy indeksami, atutomumeracją, trigerami. Zacznij więc od stworzenia tabeli. Powiedzmy że będzie mieć pola:
ID: number
APP_PARAM: varchar2(50)
PARAM_VALUE: clob
Pole 'param_value' będzie przechowywać ustawienia w formacie JSON. Póki co to pole jest po prostu polem typu CLOB - przyjmie każdą wartość. My chcielibyśmy jednak by akceptowało tylko JSON - da to nam pewność że ciąg znaków który się tam znajdzie rzeczywiście jest formatu JSON. Poniższą komendą zapewnimy sprawdzenie formatu przed umieszczeniem danych w tabeli - IS JSON contraint. Wykonujemy tę komendę w SQL Workshop => SQL Commands:
alter table app_settings
add constraint param_value_json check ( param_value is json );
Sprawdzamy czy pole tabeli sprawdza poprawność składni JSON
Jeśli wejdziesz do menu SQL Workshop => Object browser i zechcesz dodać nowy rekord do tabeli, nie będziesz w stanie. Oracle Apex nie oferuje interfejsu do wstawiania danych typu CLOB. Musisz zrobić to z poziomu SQL Workshop => SQL Commands. Wykonaj następującą komendę by umieścić testowe dane:
INSERT INTO app_settings (app_param, param_value) VALUES ('param_2', '{"eur": 4.67}');
Oracle zwóci informację że rekord został umieszczony w tabeli. Dla testu wykonaj zmianę w 'param_value'... np usuń końcowy nawias z JSON i spróbuj umieścić te dane ponownie - wykonaj komendę raz jeszcze. Zobaczysz ze Oracle nie pozwoli na to ponieważ format JSON jest niepoprawny.
Zapisywanie do tabeli z aplikacji Oracle Apex
Ustawienia naszej aplikacja będą trywialne. Potrzebujemy aktualnego kursu euro. Bedziemy go wpisywac w formularzu i przechowywac w tabeli w formacie JSON. Łatwo możesz dodać więcej pól i elementów w tym JSON.
Pod przyciskiem 'Save settings' umieść Dynamic Action (Execute Server-side code => PL SQL) o następującej składni:
DECLARE
l_exchange_rates_eur number(6,4);
BEGIN
l_exchange_rates_eur := :P501_EXCHANGE_RATE_EUR_VALUE;
UPDATE APP_SETTINGS SET param_value = '{"eur": '||l_exchange_rates_eur||'}' WHERE app_param = 'exchange_rates';
END;
Pamiętaj by przekazać wartość pola P501_EXCHANGE_RATE_EUR_VALUE do procedury (Items to submit). W powyższej procedurze tworzymy bardzo prosty JSON poprzez złączenie stringów i zawartości pola. Nie ma powodu komplikowć jeśli Twoja aplikacja będzie mieć jedynie kilka pól z ustawieniami. Miej jednak na uwadze że ten przykład tylko obrazuje proces - w realnym świecie będziesz prawdopodobnie potrzebować zrobić bardziej zaawansowaną procedurę.
Odczytywanie ustawien z tabeli
Aby wyświetlić wartość "eur" z JSON znajdującego się w tabeli, należy odczytać zawartość rekordu a następnie sparsować pole 'param_value' jako JSON. Nasz JSON nie zawiera tablic więc będzie to banalnie proste. W sekcji Pre-Rendering tworzymy nowy proces pod After Header (Local Database, PL/SQL):
DECLARE
l_eur NUMBER(5,3);
l_json VARCHAR2(500);
BEGIN
SELECT param_value INTO l_json FROM APP_SETTINGS WHERE app_param = 'exchange_rates';
apex_json.parse(l_json);
l_eur := apex_json.get_number(p_path => 'eur');
apex_util.set_session_state('P501_EXCHANGE_RATE_EUR_VALUE', l_eur);
END;
Jeśli element byłby typu string, pobierasz jego wartość używając 'get_varchar2' (np: apex_json.get_varchar2(p_path => 'jakis_string') ). Gdyby Twoj JSON zawierał tablice kod mógłby wyglądać następująco (uwaga, to pseudo kod):
DECLARE
sJsonIndex APEX_JSON.t_values;
l_response CLOB;
l_num_items NUMBER;
l_task_id VARCHAR2(80);
l_formkey VARCHAR2(5);
BEGIN
l_response := zrodlo naszego JSON...
apex_json.parse(sJsonIndex, l_response);
l_num_items := APEX_JSON.get_count(p_path => '.' , p_values => sJsonIndex);
IF l_num_items > 0 THEN
FOR i in 1 .. l_num_items LOOP
l_task_id := apex_json.get_varchar2(p_path => '[%d].id', p0 => i, p_values => sJsonIndex);
l_formkey := apex_json.get_varchar2(p_path => '[%d].formKey', p0 => i, p_values => sJsonIndex);
END LOOP;
END IF;
EXCEPTION
WHEN OTHERS THEN
RAISE;
END;