SQL, PL/SQL, JavaScript, jquery,... Quellcode in APEX clever organisieren
Andreas Wismann WHEN OTHERS Entwicklung Coaching Projektmanagement wismann@when-others.com
addresse adressee am beliebtesten! (Adressat) adresse address adress adres
Wohin mit dem Quellcode in APEX?
abwägen l e x versus KONTROLLE i t
Anwendungslogik bleibt in APEX, Geschäftslogik gehört in die Datenbank Punkt.
PL/SQL-Code in APEX... wird erst zur Laufzeit kompiliert hat keinen Status kennt keine Pragmas ignoriert Compiler Flags erscheint nicht in ALL_SOURCE, ALL_DEPENDENCIES, USER_IDENTIFIERS kann nicht mit Versions- Kontrollsystemen verwaltet werden ist nicht wiederverwendbar zur Parameterübergabe wenn keine Stored Procedures, Functions, Packages installiert werden können zum schnellen Prototyping und Ausprobieren
Code mit Geschäftslogik aus APEX herausholen,... denn nicht jeder gute SQL- oder PL/SQL-Entwickler hat gute APEX-Kenntnisse
Beispiel SELECT page_id, page_name, process_name, process_source FROM apex_application_page_proc WHERE application_name = 'Sample Database Application' AND process_type_code = 'PLSQL' AND process_name = 'Add Product to the ORDER Collection';
DB generiert Content => PL/SQL Regions Befehl HTP.PRN (... ) gibt HTML an Browser aus Befehl kann im APEX-Fenster oder im Datenbank-Programm stehen
Report: FUNCTION RETURNING SQL CLAUSE unterschätzt! Standard-Feature für Classic Report Workaround mit APEX_COLLECTIONS auch für Interaktive Reports oder warten auf APEX 5.2
Ein eigenes Package pro APEX-Seite Prozeduren und Funktionen speziell für diese Seite Nichts wiederholen: Abstützen auf Basis-Package! APEX_APP_300_P101 APEX_APP_400_P101 APEX_APPS
Validierungs-Package Nur Package Specification erforderlich enthält Dummy-Variablen oder Typdefinitionen CREATE OR REPLACE PACKAGE "APEX_APP_104" AS dummy_emp EMP%ROWTYPE; -- Tabelle EMP wird in App 104 verwendet END; Demo
oder per SQL: SELECT TYPE, NAME, dependency_type FROM user_dependencies WHERE referenced_type = 'TABLE' AND referenced_name = 'EMP';
es lebt!
NOT_IMPLEMENTED CREATE OR REPLACE PACKAGE CONST AS SUBTYPE not_implemented_t IS VARCHAR2(1); END CONST; PROCEDURE baustelle (i_param IN CONST.not_implemented_t) IS BEGIN NULL; END baustelle;
NOT_IMPLEMENTED nicht gut: IF i_param = CONST.NOT_IMPLEMENTED_VARCHAR2 THEN... END IF; besser: IF i_param IS NULL THEN... END IF;
PL/SCOPE ALTER SESSION SET PLSCOPE_SETTINGS = 'IDENTIFIERS:ALL'; -- danach Package kompilieren... SELECT name, object_type, object_name, usage, line, col FROM user_identifiers WHERE NAME LIKE 'NOT_IMPLEMENTED' '%' AND USAGE = 'REFERENCE';
Dokumentation und Kommentare SELECT t.typ, COUNT(d.*) as anzahl FROM dokumentarten t LEFT JOIN dokumente d ON (t.typ = d.typ) LEFT JOIN dokumentversand v ON (v.dok_id = d.dok_id) WHERE t.typ_name IN ('Angebot', 'Rechnung', 'Gutschrift') AND EXTRACT(YEAR FROM d.erstellung) = 2017 AND v.versanddatum IS NOT NULL AND v.aktuell = 1 GROUP BY t.typ
SELECT t.typ, COUNT(d.*) as anzahl FROM dokumentarten t LEFT JOIN dokumente d ON (t.typ = d.typ AND EXTRACT( YEAR FROM d.erstellung) = 2017) LEFT JOIN dokumentversand v ON (v.dok_id = d.dok_id AND v.versanddatum IS NOT NULL AND v.aktuell = 1) WHERE t.typ_name IN ('Angebot', 'Rechnung', 'Gutschrift') GROUP BY t.typ
-- Dokumentarten immer anzeigen, auch wenn keine -- versendeten Dokumente existieren SELECT t.typ, COUNT(d.*) as anzahl FROM dokumentarten t LEFT JOIN dokumente d ON (t.typ = d.typ AND EXTRACT( YEAR FROM d.erstellung) = 2017 LEFT JOIN dokumentversand v ON (v.dok_id = d.dok_id AND v.versanddatum IS NOT NULL AND v.aktuell = 1) WHERE t.typ_name IN ('Angebot', 'Rechnung', 'Gutschrift') GROUP BY t.typ
-- Dokumentarten immer anzeigen, auch wenn keine -- versendeten Dokumente existieren SELECT '///' as typ, 4711 as anzahl FROM dual So kann man erst einmal beginnen... die drei Schrägstriche /// markieren meine Baustellen im Projekt
Ein einziger Kommentar, gefunden in einem meterlangen Programm... IF l_cdtsav = 29 THEN l_cytsavtrt := l_cytsavtrt + 1; -- l_cytsavtrt um 1 erhöhen END IF;...
wieso? weshalb? warum? RETURN'"' REPLACE(REPLACE(CASE WHEN i_esc IS NULL THEN i_text ELSE translate(i_text,i_esc,lpad(nvl(i_to,' '),LENGTH(i_esc),' '))END, '"','"' '"'),',','"' ',' '"') '"'; Durch das Weglassen von Kommentaren oder Leerraum wird ein kompiliertes PL/SQL-Programm nicht "kleiner" oder "schneller"
JavaScript-Kommentare im Quellcode werden 1:1 an die Clients gesendet jquery langsam indiskret
Stichwort: "minify" https://jscompress.com/ https://javascript-minifier.com/ (und viele mehr...)
JavaScript in der Page gut zu finden passender Kontext aber nicht einzeln kommentierbar
Dynamic Action "springt ins Auge" Wizard-gesteuert konfigurierbar Event-getriggert jede einzelne Action ist kommentierbar!
Warum so wenig Kommentare? Kostet Zeit Eigentlich ist das selbsterklärend Wüsste nicht was man da schreiben sollte Das liest eh keiner Das Kommentarfeld in APEX ist zu weit unten => das lässt sich ändern!
Demo: Kommentarfeld im Page Designer nach oben verschieben
Kommentarfeld im Page Designer nach oben holen JavaScript soll laufen, wenn der Page Designer auf apex.oracle.com benutzt wird dazu "Tampermonkey" (oder ein anderes User Script Add-On) im Browser installieren neues User-Skript anlegen und den @match-parameter anpassen JavaScript MutationObserver API verwenden: damit wird ein DOM-Objekt auf Veränderungen überwacht (zum Beispiel durch Benutzerinteraktion oder programmatische Einflüsse) Bei jedem MutationObserver-Event (etwa ausgelöst durch das Wechseln des Items) klinken wir uns in das Rendering des Property Inspectors ein und verschieben das Kommentarfeld - von ganz unten nach ganz oben.
// ==UserScript== // @name Page-Designer // @namespace http://tampermonkey.net/ // @version 1.0 // @description Holt den Abschnitt "Comments" an die oberste Stelle im Property Inspector // @author Andreas Wismann // @match https://apex.oracle.com/pls/apex/f?p=4000:4500:* // @match https://apex.oracle.com/pls/apex/wwv_flow.accept* // @grant none // ==/UserScript== (function() { // wird nach dem vollständigen Laden des Page Designers ausgeführt function kommentarnachoben() { Abschnitt } })(); jquery('div#pe label:contains("comments")') // durch das Label "Comments".closest('div[data-group-id]') // findet man den Comments-Abschnitt,.prependTo($('div[data-group-id=1]')); // verschiebt ihn über den ersten // die Function als Reaktion festlegen, wenn Änderungen gemeldet werden: var observer = new MutationObserver(function(mutations) { }); mutations.foreach(kommentarnachoben); // Änderungen im Property Inspector überwachen // <div id="pe">...</div> ist der Property Inspector observer.observe(document.getelementbyid('pe'), {attributes: true, childlist: true, characterdata: true} ); // schon beim ersten Seitenaufbau die gewünschte Aktionen ausführen: kommentarnachoben();
Demo: View-Kommentar-App
Kommentar-App, Bestandteile: ALL_TAB_COLUMNS ALL_TAB_COMMENTS ALL_COL_COMMENTS eigens erstellte COMMENTS_V Instead-of-Update Trigger auf COMMENTS_V: EXECUTE IMMEDIATE ('COMMENT ON COLUMN... IS... '); Tabular Form / Interactive Grid über COMMENTS_V für die Eingabe und das Auslösen des Triggers Report über Kommentar-Vollständigkeit zur Motivation
Hashtags
Hashtags
Reguläre Ausdrücke zum Finden von Hashtags SELECT regexp_substr( ' -- Fachbereich konsultieren, -- @@2017-12-22 testen und abgeben SELECT * FROM... ', '@{2}(20)?[0-9]{2}-[0-1]?[0-9](-[0-3]?[0-9])?.*' ) FROM dual;
zentralisieren... CREATE PACKAGE regex AS END; CREATE PACKAGE BODY regex AS END;
CREATE OR REPLACE PACKAGE "REGEX" AS /** Speichert häufig verwendete Reguläre Ausdrücke */ SUBTYPE regular_expression IS VARCHAR2(255 CHAR); -- 255 reicht für den Anfang... hashtag_ hashtag_datum_ hashtag_label_ CONSTANT regular_expression := '@@'; -- mit dieser Startsequenz beginnt jeder Hashtag. -- Man könnte auch '#' nehmen, doch das wird in APEX bereits für Substitutionen verwendet, -- sowie in CSS für das Attribut "id"; in SQL und PL/SQL ist es ein erlaubter Bestandteil -- von Bezeichnern. Insofern wäre das # keine gute Wahl für unsere Zwecke. CONSTANT regular_expression := hashtag_ '(20)?[0-9]{2}-[0-1]?[0-9](-[0-3]?[0-9])?.*'; CONSTANT regular_expression := hashtag_ '[[:alnum:]]+'; -- Für SQL-Abfragen liefern diese Funktionen die regulären Ausdrücke zurück: FUNCTION hashtag FUNCTION hashtag_datum FUNCTION hashtag_label RETURN regular_expression DETERMINISTIC; -- die Hashtag-Startsequenz RETURN regular_expression DETERMINISTIC; -- erkennt z.b. @@2018-01 plus Rest der Zeile RETURN regular_expression DETERMINISTIC; -- erkennt z.b. @@todo (ohne den Rest) END "REGEX";
CREATE OR REPLACE PACKAGE BODY "REGEX" AS -- @@2017-11 Auf der DOAG 2017 präsentieren: FUNCTION hashtag RETURN regular_expression DETERMINISTIC IS BEGIN RETURN hashtag_; END; FUNCTION hashtag_datum RETURN regular_expression DETERMINISTIC IS BEGIN RETURN hashtag_datum_; END; FUNCTION hashtag_label RETURN regular_expression DETERMINISTIC IS -- @@todo weitere Hashtag-Muster definieren END "REGEX"; BEGIN RETURN hashtag_label_; END;
Meine Termine? SELECT * FROM user_source WHERE regexp_like(text, regex.hashtag_datum);
Wo steht Code mit Hashtags? SELECT NAME, TYPE, line, TRIM(text) AS text FROM USER_SOURCE WHERE REGEXP_LIKE (text, regex.hashtag_label);
Welche Hashtags verwenden die Entwickler am häufigsten? WITH all_hashtags AS (SELECT substr(hashtag, 1 + LENGTH(regex.hashtag)) AS hashtag -- "@@" abschneiden FROM (SELECT lower(regexp_substr(text, regex.hashtag_label)) AS hashtag FROM user_source s) WHERE hashtag IS NOT NULL) SELECT hashtag, COUNT(*) FROM all_hashtags GROUP BY hashtag ORDER BY count(*) desc, hashtag;
Eine eindeutige ID für alles und jedes... @@4711 findet "alles, was damit zu tun hat"
Cherry Keypad G84-4700 Die PS/2-Version speichert die Programmierung im Flash-ROM Demo: Cherry-Tastenbelegung
ExpertKeys EK-58
Steelseries APEX 350 (nomen est omen?) Demo: Konfigurations-Software
Demo: PL/SQL Developer Documentation, CODEGENERATOR für LOGGER
Buchtipp Jürgen Sieben: Oracle APEX 895 Seiten APEX 5.1, September 2017 79,95 EUR
Andreas Wismann WHEN OTHERS Entwicklung Coaching Projektmanagement wismann@when-others.com