Hlavní navigace

Programování v JavaFX: PG funkce, pohledy a kontrola jejich přítomnosti

22. 10. 2015
Doba čtení: 8 minut

Sdílet

Minulý osmý díl by věnován pokročilejším funkcí, jako jsou pohledy a uložené procedury. Také jsme si zatím pouze okrajově ukázali, jak tyto funkce využít v rámci aplikace JavaFX. V dnešním devátém dílu se budeme věnovat další práci s PG funkcemi a ukážeme si zobrazení tabulkových dat pomocí pohledů.

Minulý díl jsme zakončili vytvořením procedur a funkcí pro kontrolu přítomnosti dvou potřebných PG funkcí. V dnešním dílu na to navážeme a uděláme další krok v kontrole „úplnosti“ vybavení tím, že zkontrolujeme přítomnost jednoho z databázových pohledů. Využijeme k tomu opět dotaz za pomoci systémových funkcí a již minule vytvořenou funkci, která vrací logické hodnoty. Oproti minule si ale docela zásadně přepracujeme způsob volání všech kontrol. Pokud bychom ho totiž ponechali v proceduře initialize, tak bychom velmi brzy narazili na problém s odkazem na widget Stage. Ten totiž není ještě aktivovaný a nelze se na něj odkazovat. Proto uděláme tři jednoduché změny:

  1. v proceduře initialize ponecháme pouze příkazy, které umožní v otevřeném okně použít pouze jedno vyhrazené tlačítko
  2. do FXML souboru přidáme název pro hlavní kořenový widget AnchorPane a také nové tlačítko včetně příslušné akce
  3. pod nové tlačítko umístíme veškeré plánované kontroly včetně řádného přihlášení k databázi. Pokud proběhnou kontroly bez problémů, uvolní se k použití ostatní tři tlačítka pro zobrazení údajů z pohledů

Pro první změnu můžeme použít poměrně jednoduchou úpravu a to dokonce dvojího druhu. Princip spočívá v tom, že pouze tlačítko pro spuštění bude aktivní, ostatní tři budou deaktivovaná. Pro tento účel můžeme použít dva příkazy. Ten první příslušné tlačítko úplně „zneviditelní“ (či naopak):

button1.setVisible(false);

Druhá možnost ponechá tlačítko viditelné (jenom zašedlé), ale neaktivní a nepoužitelné:

button1.setDisable(true);

Druhá změna je velmi jednoduchá, a tak k ní uvedeme pouze stručnou poznámku: po přidání nového widgetu a uvedených názvů se změny uloží a zobrazíme si kostru kontroléru. Nemusíme jí ale kopírovat celou, stačí použít kopii pouze tří nových řádků a ty umístit do třídy samexam3.java. Do procedury initialize dáme pouze 4 příkazy:

button1.setDisable(true);
button2.setDisable(true);
button3.setDisable(true);
button4.setDisable(false);

Vše ostatní smažeme a vrhneme se do tvorby kódu pro kontrolu v tom smyslu, který jsme si dříve nastínili. Pro jeho kompletaci musíme udělat 4 základní kroky:

  1. do privátních deklarací přidáme deklaraci Stage:
    private Stage stage = new Stage();
  2. vytvoříme novou proceduru pro zavření okna:
    private void onClose() { stage = (Stage) contentPane.getScene().getWindow(); stage.close(); }
  3. vytvoříme zatím prázdnou proceduru pro vlastní kód kontrol:
    private void kontrola() {}
  4. tuto novou proceduru budeme volat z příslušného tlačítka, resp. akce po kliknutí na něj:
    @FXML void btn4_Click(ActionEvent event) { kontrola(); }

Funkce pro kontrolu existence samotných PG funkcí jsme vytvořili již v minulém dílu. Nyní bude nutné vytvořit další funkce, pomocí kterých zkontrolujeme ne sice přítomnost hliníku, ale jednoho pohledu a příslušné tabulky. Pro zadání dotazu budeme muset použít jiné systémové funkce PG Information Schema. Kromě toho budeme vycházet ze skutečnosti, že z pohledu databáze jsou tabulky a pohledy vlastně to samé. Proměnná s dotazem na existenci pohledu tedy bude vypadat následovně:

private String checkView = "SELECT EXISTS ( SELECT 1  FROM information_schema.tables " +
     "WHERE table_catalog='fxguidedb' AND table_schema='public' AND table_name='pohled1');";

Pro dotaz na tabulku se proměnná moc nezmění:

private String checkTable = "SELECT EXISTS ( SELECT 1  FROM information_schema.tables " +
     "WHERE table_catalog='fxguidedb' AND table_schema='public' AND table_name='pocty');";

Abychom mohli v případě potřeby spustit uloženou PG funkci a tím uložit i příslušné pohledy do databáze, musíme vytvořit proceduru, která to zajistí. Nebude se sice moc lišit od těch již dříve použitých, ale přece jenom to změna bude. Pro lepší přehled uvedeme také funkci pro ověření přítomnosti PG funkcí a obě funkce porovnáme.

private boolean checkItem (final String query) {        //1
        boolean item = false;
        PreparedStatement pStat = null;     //2
        CONN = connDB("fxguide", "fxguide");        //3
        try { pStat = CONN.prepareStatement(query);     //4
            ResultSet rs = pStat.executeQuery();        //5
            while (rs.next()) { item = rs.getBoolean(1); }      //6
        } catch (SQLException ex) { ex.getMessage(); }      //7
        finally { if (pStat!=null) {        //8
                try { pStat.close(); } catch (SQLException ex) { ex.getMessage(); }     //9
                try { CONN.close(); } catch (SQLException ex) { ex.getMessage(); } } }      //10
        return item; }      //11
public void runPGF(final String pgfName){       //1
        CallableStatement callStat = null;      //2
        CONN = connDB("fxguide", "fxguide");        //3
        try { callStat = CONN.prepareCall("{call "+pgfName+"()}");      //4
            callStat.execute();     //5
            callStat.close(); }     //6
        catch (SQLException e) { e.getMessage();        //7
        } finally { if (callStat != null) {     //8
            try { callStat.close(); }       //9
            catch (SQLException e) { e.getMessage(); }      //10
            try { CONN.close(); }       //11
            catch (SQLException e) { e.getMessage(); } } } }        //12

Jak je z kódů zřejmé, nemá smysl nijak rozebírat řádky 7 – 12, začátek ale trochu rozebereme:

  • 1. řádek – funkce se zde samozřejmě odlišuje uvedením typu návratové hodnoty. Rozdíl je také v tom, že ve funkci se zadává celý text dotazu a v proceduře pouze název PG funkce, která se má spustit
  • 2. řádek – zde se nachází jedna z „velkých“ odlišností, a sice deklarace základní proměnné. Ve funkci se jedná o typ PreparedStatement Prepared Statement, v proceduře se ale použije úplně jiný typ proměnné: Callable Statement. Oba typy proměnných jsou součástí JDBC driveru pro PG, jak je zřejmé z odkazů.
  • 3. řádek – je stejný a slouží pro připojení k databázi
  • 4. řádek – zde je opět odlišnost ve stylu volání příslušné funkce. Podrobnosti o volání PG funkcí pomocí JDBC jsou v předchozím odkazu včetně těch funkcí, které mají parametry nebo vracejí nějaké hodnoty. K nim se také dostaneme později v dalších dílech
  • 5. řádek – provedení příkazu se také trochu liší, u PG funkce není nutné používat žádný dotaz
  • 6. řádek – u PG funkce se příkaz pouze ukončí, protože nevrací žádnou hodnotu

Všechno je již připravené a my můžeme vytvořit poslední proceduru, která provede všechny potřebné kontroly:

private void kontrola() {
        if (!logOK()) { dialogMessage("Neproběhlo řádné přihlášení, okno bude ukončeno!","Přihlášení do databáze",'E'); onClose(); }      //1
        else if (!checkItem(checkF1) || !checkItem(checkF2)) {      //2
            dialogMessage("Potřebné PG funkce nejsou k dispozici, okno bude ukončeno!","Kontrola PG funkcí",'E'); onClose(); }  //3
        else if (!checkItem(checkView)) { Integer qm = questMessage("Pohled není k dispozici! Ano - uloží pohled do DB, Ne - zavře okno",
                "Kontrola DB pohledu");     //4
                if (qm==1) onClose();       //5
                    else { runPGF("initview"); kontrola(); }}       //6
        else if (!checkItem(checkTable)) { Integer qm = questMessage("Tabulka není k dispozici! Ano - uloží tabulku do DB, Ne - zavře okno",
                "Kontrola DB tabulky");     //7
            if (qm==1) onClose();       //8
            else { runPGF("initdata"); kontrola(); }}       //9
        else {
            dialogMessage("Proběhlo řádné přihlášení, databáze je přístupná","Přihlášení do databáze",'I');        //10
            dialogMessage("Obě potřebné funkce jsou v databázi k dispozici", "Kontrola PG funkcí", 'I');       //11
            dialogMessage("Potřebné pohledy jsou v databázi k dispozici", "Kontrola DB pohledů", 'I');      //12
            dialogMessage("Potřebná tabulka je v databázi k dispozici", "Kontrola DB tabulky", 'I');     //13
            button1.setDisable(false);      //14
            button2.setDisable(false);      //15
            button3.setDisable(false);      //16
            button4.setDisable(true); }}        //17

Jak je z kódu patrné, kontrolních funkcí přibylo a bylo by dobré si vše okomentovat. Než se do toho pustíme, tak rozebereme hrubší členění celé funkce:

  • řádky 1 – 9 se zabývají situacemi, kdy se v kontrole objeví nějaký problém, na který aplikace reaguje výstražným dialogem a také příslušnou funkcí – ukončením okna nebo nabídkou nějakého řešení
  • řádky 10 – 13 postupně ukazují informační dialog o tom, že je vše v pořádku a kontrola proběhla bez závad
  • řádky 14 – 17 nastavují možnosti spuštění kontroly nebo zobrazení jednotlivých pohledů do databáze

nyní již může následovat podrobnější komentář jednotlivých řádků:

  1. řádek – kontroluje se přihlášení do databáze. Pokud není přihlášení úspěšné, nemá cenu dál nic kontrolovat a je třeba zavřít okno
  2. řádek – pokud je přihlášení úspěšné, provádí se kontrola přítomnosti PG funkcí (jedné z nich).
  3. řádek – pokud není alespoň jedna PG funkce přítomná, okno se ukončí
  4. řádek – pokud není k dispozici příslušný pohled, zobrazí se dialogové okno s možností volby uložení pohledu nebo zavření okna
  5. řádek – pokud je vybrána volba pro ukončení, okno se zavře
  6. řádek – pokud je vybrána volba pro uložení pohledu do databáze, spustí se příslušná PG funkce a kontrola se rekurzivně spustí od začátku
  7. řádek – pokud není k dispozici příslušná tabulka, zobrazí se dialogové okno s možností volby pro uložení tabulky nebo zavření okna
  8. řádek – pokud je vybrána volba pro ukončení, okno se zavře
  9. řádek – pokud je vybrána volba pro uložení tabulky do databáze, spustí se příslušná PG funkce a kontrola se rekurzivně spustí od začátku
  10. řádek – zobrazí se dialog o řádném přihlášení do databáze
  11. řádek – zobrazí se dialog o přítomnosti PG funkcí
  12. řádek – zobrazí se dialog o přítomnosti pohledů
  13. řádek – zobrazí se dialog o přítomnosti tabulky
  14. řádek – aktivuje se přístup k tlačítku pro spuštění prvního pohledu
  15. řádek – aktivuje se přístup k tlačítku pro spuštění druhého pohledu
  16. řádek – aktivuje se přístup k tlačítku pro spuštění třetího pohledu
  17. řádek – tlačítko pro spuštění kontroly je deaktivováno

První obrázkové galerie pak ukazuje všech osm možných dialogových oken, které se při provádění kontroly mohou před námi objevit.

Pokud nám proběhla kontrola úspěšně a bez chybových hlášení, můžeme pozorovat změny ve vzhledu okna. První obrázek ve druhé galerii nám ukazuje, jak vypadá formulářové okno před spuštěním kontrolní procedury, druhý obrázek druhé galerie pak stav po úspěšném ukončení kontroly. Na to, abychom ale mohli příslušná tlačítka použít a zobrazit příslušné pohledy, musíme ještě přidat nějaký ten kód. Vlastně se jedná o několik málo akcí:

  1. do deklarací tabulek a sloupců z FXML souboru doplníme příslušné typy:
        @FXML private TableView<ObservableList<ObservableList>> table1;
        @FXML private TableColumn<ObservableList<String>, String> col11;
        ...
  2. do aktuální třídy zkopírujeme z té předchozí (samexam2.java) tři potřebné funkce: dataView, initTableColumn, alignTableColumn
  3. vytvoříme tři nové procedury (showData1, showData2, showData3) pro volání funkcí zobrazení tabulkových dat včetně inicializace sloupců, nastavení typu zobrazovaných dat a zarovnání hodnot v tabulce. V těchto procedurách musíme nastavit správný počet a typ zobrazovaných údajů, správný název tabulky a jednotlivých sloupců a jejich zarovnání. Zadávaný dotaz se díky použití pohledů velmi zjednoduší:
    final String query = "SELECT * FROM pohled1;";
  4. volání těchto procedur přidáme do příslušných tlačítkových akcí

Po provedení výše uvedených změn je možné aplikaci přeložit, spustit a postupně zobrazit data ze všech tří definovaných pohledů. Jak to vypadá konkrétně, ukazují poslední tři obrázky ve druhé galerii. Kompletní kód příslušné procedury je v příloze samexam3.java.

root_podpora

Tímto bychom mohli ukončit nejen dnešní díl, ale také jistou část našeho kurzu. Od příštího dílu se budeme věnovat další části.

V dnešním dílu ukončíme kontrolu přítomnosti potřebných součástí databáze a ukážeme si zobrazení tabulkových dat pomocí pohledů. Od příštího dílu se začneme věnovat nejen zobrazení ukázkových dat, ale i jejich změnám – přidávání nových, mazání stávajících a změny aktuálních záznamů a hodnot.

Byl pro vás článek přínosný?