Hlavní navigace

Programování v JavaFX: zobrazení a formátování dat z databázové tabulky (2)

Jaromír Vojtaj

Minulý šestý díl byl zaměřen na kód potřebný pro základní zobrazení dat z tabulky ve formuláři a nastínil také možná obecnější řešení této problematiky. V dnešním sedmém dílu budeme obecnější řešení implementovat a komentovat a konečně si ukážeme kompletní obsah tabulky ukázkových dat.

Na konci minulého dílu jsme si jednak rozebrali základní předpoklady pro možnosti obecnějšího řešení dané úlohy a také si připravili vše potřebné pro implementaci nějakého konkrétního kódu. Proto nebudeme dlouho otálet a ukážeme si kód procedury pro čtení dat a jejich uložení do lokální proměnné:

private ObservableList<ObservableList> dataView (final String query, final Integer[] cols){                               --1
    ObservableList<ObservableList> data = FXCollections.observableArrayList();                                    --2
        PreparedStatement pStat = null;                                                         --3
        String item = null;                                                             --4
        Integer type = null;                                                                --5
        Double dbl = null;                                                              --6
        CONN = connDB("fxguide", "fxguide");                                                        --7
        try { pStat = CONN.prepareStatement(query);                                                 --8
        ResultSet rs = pStat.executeQuery();                                                    --9
            while (rs.next()) {                                                             --10
        ObservableList<Object> row = FXCollections.observableArrayList();                                 --11
                for (int i=1;i<=cols.length;i++){                                                    --12
        item=rs.getObject(i).toString();                                                    --13
        type=cols[i-1];                                                             --14
                    switch (type) { case 0: row.add(item); break;                                           --15
                                    case 1: row.add(item.replaceAll("\\s+$", "")); break;                               --16
                                    case 2: dbl=Double.valueOf(item); row.add(nform2.format(dbl)); break;                       --17
                                    case 3: row.add(item.substring(8) + "." + item.substring(5, 7) + "." + item.substring(0, 4)); break;        --18
                                    case 4: dbl=Double.valueOf(item);  row.add((dbl < 0.001 && dbl >-0.001) ? "" : nform2.format(dbl)); break; } }    --19
                data.add(row); }                                                            --20
                } catch (SQLException e) { e.getMessage()); }                                               --21
                finally {                                                               --22
            if (pStat != null) {                                                        --23
                try { pStat.close(); }                                                  --24
                catch (SQLException e) { e.getMessage(); }                                      --25
                try { CONN.close(); }                                                   --26
                catch (SQLException e) { e.getMessage; }} }                                     --27
                return data;                                                        --28
                }

Kódu je ve funkci celkem dost (ale jenom o 6 řádků více, než v původní verzi v pátém dílu, tedy 28 vs 22), takže se na něj podíváme trochu podrobněji:

  • 1. do parametrů funkce přibylo pole celočíselných hodnot. Jejich význam si ukážeme později
  • 2. zde je vše při starém
  • 3. ani tady se nic nemění
  • 4. nová deklarace textové proměnné
  • 5. nová deklarace celočíselné proměnné
  • 6. nová deklarace desetinné proměnné
  • 7. již známé přihlášení k databázi
  • 8. beze změny, příprava dotazu
  • 9. provedení dotazu a uložení do příslušné proměnné také beze změn
  • 10. start procházení výsledků dotazu je také stejný
  • 11. deklarace řádku údajů opět ve známém formátu
  • 12. zahájení smyčky FOR je nové a souvisí se změnou funkce na obecnou. Prochází se přes deklarovanou proměnnou, která představuje počet sloupců, které jsou výsledkem dotazu a vyjadřuje také jejich typ
  • 13. do deklarované textové proměnné se uloží výsledek dotazu pro každý sloupec v obecném formátu Object a převede se na typ String
  • 14. do deklarované proměnné se uloží požadovaný typ (vlastně budoucí formát zobrazení) příslušného sloupce výsledné datové sady
  • 15. pomocí této proměnné se určí typ sloupce, nastaví se jeho formát a tato hodnota se uloží do proměnné pro každý řádek výsledného dotazu. Pro typ 0 se neděje nic a hodnota se uloží jako String
  • 16. typ 1 znamená oříznutí nadbytečných prázdných znaků zprava pomocí deklarované funkce
  • 17. typ 2 znamená převod na formát pro uložení např, finančních údajů – desetinné číslo se dvěma viditelnými desetinnými místy
  • 18. typ 3 je pak formátován jako běžné datum v národním formátu – DD.MM.YYYY
  • 19. typ 4 nahradí malá desetinná čísla prázdným znakem (opět se může hodit pro nějaké finanční a peněžní hodnoty). Za tímto formátováním končí jednak přepínač podle typu sloupce, ale také smyčka pro všechny sloupce dotazu
  • 20. uložením jednotlivých řádků do proměnné s obsahem datové sady se také ukončí procházení výsledků dotazu
  • 21. – 28. ukončení funkce se zachycením výjimek a návratová proměnné jsou opět stejné

Další změnu uděláme v proceduře pro inicializaci jednotlivých sloupců tabulky. Jak již bylo řečeno, zde je zobecnění mnohem jednodušší:

private void initTableColumn(final TableColumn[] column) {                                              --1
        for (int i=0;i<column.length;i++) {                                                      --2
    final int finalI = i;                                                               --3
            column[i].setCellValueFactory(new Callback<TableColumn.CellDataFeatures<ObservableList<String>, String>, ObservableValue<String>>() {   --4
                @Override
                public ObservableValue<String> call(TableColumn.CellDataFeatures<ObservableList<String>, String> param) {             --5
                    return new SimpleStringProperty(param.getValue().get(finalI));                                  --6
                    } }); } }

Zde přibyl pouze jeden řádek proti původní verzi, kterou jsme ale nekomentovali. Proto to uděláme teď:

  • 1. do parametrů se přidá deklarace pole s typem tabulkového sloupce
  • 2. zahájí se smyčka přes počet deklarovaných sloupců
  • 3. definuje se lokální proměnná tak, aby bylo možné použít parametr smyčky v dalším kódu funkce
  • 4. konkrétní sloupec je zde nahrazen hodnotou deklarovaného pole a všechny parametry jsou typu String bez ohledu na původní typ sloupce
  • 5. i zde jsou všechny parametry typu String
  • 6. vzhledem k tomu, že jsou předchozí parametry všechny stejné, je možné zde použít pouze jeden typ funkce a přiřadit příslušný sloupec pomocí deklarované proměnné

Poslední novou procedurou je pak procedura na zarovnání tabulkových hodnot ve sloupcích. Ani zde nebylo zobecnění nijak složité:

private void alignTableColumn(final TableColumn[] column, final Integer[] pos) {                                --1
        for (int i=0;i<column.length;i++) {                                                  --2
            final int finalI = i;                                                   --3
            column[i].setCellFactory(new Callback<TableColumn<ObservableList<String>, String>, TableCell<ObservableList<String>, String>>() { --4
                @Override
                public TableCell<ObservableList<String>, String> call(TableColumn<ObservableList<String>, String> param) {          --5
                    TableCell<ObservableList<String>, String> tc = new TableCell<ObservableList<String>, String>() {                --6
                        @Override
                        public void updateItem(String item, boolean empty) {                                    --7
                            if (item != null) { setText(item); } } };                       --8
                    switch (pos[finalI]) {                                                  --9
                        case 1: tc.setAlignment(Pos.CENTER_LEFT); break;                        --10
                        case 2: tc.setAlignment(Pos.CENTER); break;                                     --11
                        case 3: tc.setAlignment(Pos.CENTER_RIGHT); break; }                                 --12
                        return tc;                                                      --13
                        } }); } }

Zde jsou nově tři řádky a podrobnější komentář následuje:

  • 1. do parametrů se přidají deklarace pole tabulkových sloupců a celočíselné pole
  • 2. zahájí se smyčka přes všechny sloupce tabulky
  • 3. definuje se lokální proměnná tak, aby bylo možné použít parametr smyčky v dalším kódu funkce
  • 4. pro každý sloupec se nastaví příslušná vlastnost, všechny parametry jsou opět textové
  • 5. – 8. další deklarace jsou prakticky stejné
  • 9. pro přepínač se použije deklarovaná proměnná pole s parametrem smyčky
  • 10. pro hodnotu 1 se provede zarovnání nalevo
  • 11. pro hodnotu 2 se provede zarovnání na střed
  • 12. pro hodnotu 3 se provede zarovnání napravo
  • 13. návratová hodnota je stejná

Nové verze procedur už sice máme připravené, ale naše snažení ještě není na úspěšném konci. Už jenom z toho důvodu, že IJI hlásí spoustu chyb. Některé z nich je možné odstranit importem potřebných knihoven, ale určitě ne všechny. Hlavně chyby v posledních dvou procedurách mají souvislost s deklarací tabulky a jejích sloupců. Už bylo dříve řečeno, že všechny sloupce budou deklarovány úplně stejně (typ String) bez ohledu na jejich typ. Pro lepší přehled si sloupce také seřadíme:

@FXML private TableView<ObservableList<ObservableList>> table_Obce;
@FXML private TableColumn<ObservableList<String>, String> col_ID;
@FXML private TableColumn<ObservableList<String>, String> col_Ckraje;
@FXML private TableColumn<ObservableList<String>, String> col_Kraj;
@FXML private TableColumn<ObservableList<String>, String> col_COkresu;
@FXML private TableColumn<ObservableList<String>, String> col_Okres;
@FXML private TableColumn<ObservableList<String>, String> col_Obec;

Ani tohle ale ještě nevede k vítěznému závěru a IJI pořád tvrdošíjně lpí na chybách. Proto musíme do třídy samexam2 zkopírovat z předchozí verze třídy samexam1 jednak obě procedury na připojení k databázi a kontrolu úspěšnosti tohoto připojení (conDB, logOK) a také obě globální proměnné:

private Connection CONN = null;
private String host = "jdbc:postgresql://127.0.0.1:5432/fxguidedb";

K tomu musíme přidat ještě funkci pro formátování hodnot v desetinném tvaru:

public NumberFormat nform2 = new DecimalFormat("#,##0.00");

Toto je samozřejmě pouze jedna z možností a ukázka formátování. Obecně lze asi konstatovat, že výše uvedené obecné provedení dává tvůrcům a uživatelům poměrně dobré a jednoduché nástroje pro formátování výsledných datových sad dle jejich potřeb.

Poslední dvě změny, které musíme udělat, je vytvoření procedury pro volání všech výše uvedených funkcí a procedur a spuštění této procedury při otevření formuláře:

 private void showData() {
        final String query = "SELECT * FROM obce ORDER BY ckraje, cokresu;";        --1
        final ObservableList data = dataView(query, new Integer[]{1,0,1,0,1,1});        --2
        final TableColumn[] tc = new TableColumn[]{col_ID, col_Ckraje, col_Kraj, col_Cokresu, col_Okres, col_Obec}; --3
        initTableColumn(tc);        --4
        alignTableColumn(tc, new Integer[]{2,2,1,2,1,1});       --5
        table_Obce.setItems(data);      --6
    }

Řádků sice není příliš mnoho, ale přesto k nim přidáme krátký komentář:

  • 1. v definici dotazu bylo použito řazení dle čísel krajů a okresů
  • 2. datová sady pro zobrazení je deklarována pomocí předchozího dotazu a proměnné, kde je celkem 5 sloupců s deklarací požadovaného typu (formátu) zobrazení
  • 3. pole tabulkových sloupců je deklarováno pomocí proměnných z FXML
  • 4. inicializace sloupců tabulky je volaná pouze jedním příkazem
  • 5. zarovnání zobrazených hodnot v tabulce stačí volat také pouze jednou. jako parametry jsou použitá pole pro definici tabulkových sloupců a pole pro typ jejich zarovnání v zobrazované tabulce
  • 6. přiřazení darové sady příslušnému widgetu je pak opět stejné, jako v úvodním příkladu

Nyní už by měl být vše v pořádku, takže můžeme změny v kódu uložit a přeložit a spustit příslušnou aplikaci. Když si vybereme nově definovanou úlohu, tak uvidíme, že je vše v pořádku a dostaneme výsledek, který je viditelný na následujícím obrázku:

V příloze jsou také příslušné nové třídy: samExam2.fxmlsame­xam2.java.

V dnešním dílu jsme představili konkrétní obecnější řešení implementace a komentovali podrobněji celý kód. Konečně jsme si také ukázali kompletní obsah tabulky ukázkových dat. V příštím dílu se budeme věnovat pokročilejším funkcím PG jako jsou pohledy a uložené procedury. Samozřejmě si také ukážeme, jak tyto funkce využít v rámci aplikace JavaFX.

Našli jste v článku chybu?

13. 10. 2015 10:40

ava (neregistrovaný)

Ano, desktopove appky. Oficialne je podporovano prinejmensim to same, co podporoval SWING, neoficialne to vypada i na android a iphone (nezkousel jsem, ma s tim nekdo zkusenosti?), to mi neprijde zas tak malo. Vyvijet s JavaFX webovky me vubec nenapadlo....

12. 10. 2015 21:21

atarist (neregistrovaný)

No staci se podivat na seznam oficialne podporovanych systemu, tedy nic moc. Ale co je horsi - jak chcete delat RIA, kdyz Oracle vlastne mlci na ukoncujici se podporu NPAPI? Nebo se chystate na prepis desktopove appky?

Lupa.cz: Kdo pochopí vtip, může jít do ČT vyvíjet weby

Kdo pochopí vtip, může jít do ČT vyvíjet weby

120na80.cz: 5 nejčastějších mýtů o kondomech

5 nejčastějších mýtů o kondomech

Vitalia.cz: Vychytané vály a válečky na vánoční cukroví

Vychytané vály a válečky na vánoční cukroví

Lupa.cz: Propustili je z Avastu, už po nich sahá ESET

Propustili je z Avastu, už po nich sahá ESET

Měšec.cz: Zdravotní a sociální pojištění 2017: Připlatíte

Zdravotní a sociální pojištění 2017: Připlatíte

120na80.cz: Pánové, pečujte o svoje přirození a prostatu

Pánové, pečujte o svoje přirození a prostatu

Měšec.cz: Kdy vám stát dá na stěhování 50 000 Kč?

Kdy vám stát dá na stěhování 50 000 Kč?

Podnikatel.cz: Podnikatelům dorazí varování od BSA

Podnikatelům dorazí varování od BSA

Podnikatel.cz: Snížení DPH na 15 % se netýká všech

Snížení DPH na 15 % se netýká všech

Vitalia.cz: Mondelez stahuje rizikovou čokoládu Milka

Mondelez stahuje rizikovou čokoládu Milka

Podnikatel.cz: Chaos u EET pokračuje. Jsou tu další návrhy

Chaos u EET pokračuje. Jsou tu další návrhy

Vitalia.cz: Taky věříte na pravidlo 5 sekund?

Taky věříte na pravidlo 5 sekund?

Podnikatel.cz: Udávání a účtenková loterie, hloupá komedie

Udávání a účtenková loterie, hloupá komedie

120na80.cz: Horní cesty dýchací. Zkuste fytofarmaka

Horní cesty dýchací. Zkuste fytofarmaka

Podnikatel.cz: K EET. Štamgast už peníze na stole nenechá

K EET. Štamgast už peníze na stole nenechá

Vitalia.cz: Jmenuje se Janina a žije bez cukru

Jmenuje se Janina a žije bez cukru

Podnikatel.cz: Chtějte údaje k dani z nemovitostí do mailu

Chtějte údaje k dani z nemovitostí do mailu

DigiZone.cz: Recenze Westworld: zavraždit a...

Recenze Westworld: zavraždit a...

Vitalia.cz: Paštiky plné masa ho zatím neuživí

Paštiky plné masa ho zatím neuživí

120na80.cz: Co všechno ovlivňuje ženskou plodnost?

Co všechno ovlivňuje ženskou plodnost?