Programování v JavaFX: vkládání nových záznamů do tabulky

Jaromír Vojtaj 28. 1. 2016

Minule jsme zahájili novou kapitolu o Hibernate ORM včetně konfigurace a jednoduchého zobrazení tabulkových dat. Dnes se zaměříme na zobrazení dat v tabulce a ukážeme si dvě varianty mazání a aktualizace záznamů.

minulém dílu jsme si ukázali konfiguraci Hibernate a na jejím základě také jednoduché zobrazení tabulkových dat v konzole. Kromě výpisu jednotlivých položek nebylo toto zobrazení nijak moc „čitelné“. Pro nás to ale není úplně podstatné, protože naším cílem je zobrazit data ve widgetu tabulky. Proto vyjdeme z předchozích pokusů a vytvoříme si výkonnou proceduru, která to zajistí. Jako první musíme změnit formát widgetů tabulky a jejích sloupců. Tentokrát použijeme formát, který jsme použili v příkladech s SQL dotazem (uvedeme pouze tabulku a jeden sloupec, ostatní jsou stejné):

@FXML private TableView<ObservableList<ObservableList>> table1;
@FXML private TableColumn<ObservableList<String>, String> col1;

Také z verze SQL zkopírujeme proceduru na inicializaci tabulkových sloupců – initTableColumn. Naopak z posledního dílu o JOOQ použijeme novou verzi procedury na zarovnání údajů ve sloupcích tabulky – alignTableColumn. Pak už můžeme přikročit k vytvoření výkonné funkce na zobrazení výsledků dotazů z tabulky. Použijeme v ní vlastně princip „zkřížení“ výpisu jednotlivých hodnot z minulého dílu a obecné postupy, které jsme použili při SQL dotazech. Víc asi objasní samotný kód a komentář k němu:

private ObservableList<ObservableList> dataView() {                       //1
        ObservableList<ObservableList> data = FXCollections.observableArrayList();        //2
        SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();  //3
        Session session = sessionFactory.openSession();                     //4

        Query query = session.createQuery("FROM udaje ORDER BY id");                //5

        for (Iterator iterator = query.iterate(); iterator.hasNext();) {            //6
            udaje udaj = (udaje) iterator.next();                       //7
            ObservableList<Object> row = FXCollections.observableArrayList();         //8

            row.add(udaj.getId().toString());                           //9
            row.add(udaj.getCelecis().toString());                      //10
            row.add(udaj.getDescis().toString());                       //11
            row.add(udaj.getMaledes().toString());                      //12
            row.add(udaj.getRetezec());                             //13
            row.add(udaj.getDatum().toString());                        //14
            data.add(row); }                                    //15

        System.out.println(data.toString());                            //16

        session.flush();                                    //17
        sessionFactory.close();                                 //18
        return data; }                                      //19

Jak je z kódu patrné, opravdu se jedná o spojení dvou již dříve použitých funkcí, takže jenom namátkový komentář:

  • 1. řádek – funkce má stejný typ, jako měla ta v SQL verzi
  • 2. řádek – deklaruje se sumární lokální proměnná stejného typu
  • 3. – 5. řádek – konfigurace a zahájené sezení, dotaz na tabulku, rovnou s řazením podle klíčové položky
  • 6. – 7. řádek – začátek iterace přes všechny hodnoty ve výsledku dotazu
  • 8. řádek – deklarace lokální proměnné pro uložení hodnot z jednoho záznamu v tabulce
  • 9. – 14. řádek – uložení jednotlivých položek v záznamu do lokální proměnné
  • 15. řádek – uložení řádku do sumární proměnné
  • 16. řádek – výpis sumární proměnné do konzole
  • 17. – 18. řádek – ukončení sezení
  • 19. řádek – návratová hodnoty funkce

Mohli bychom sice funkci připojit k některému z tlačítek, ale uděláme si rovnou volací proceduru a tu přidáme do procedury initialize. Volací procedura bude mít asi očekávaný formát:

private void viewTable() {
        final ObservableList data = dataView();
        final TableColumn[] tc = new TableColumn[]{col1, col2, col3, col4, col5, col6};
        initTableColumn(tc);
        alignTableColumn(tc, new String[]{"CA", "RA", "RA", "RA", "LA", "CA"});
        table1.setItems(data); }

Po spuštění aplikace můžeme na prvním obrázku v galerii vidět jednat výpisy, které do konzole „dodává“ Hibernate, a také celkem čitelný výpis všech položek z dotazu. Druhý obrázek galerie pak ukazuje zobrazení údajů v tabulce. Dále už se nebudeme zobrazením údajů v tabulce zabývat a přejdeme rovnou k další části CRUD – mazání záznamů. Před vlastní zkouškou musíme ještě přidat nějaké náležitosti (všechno pomocí kopírování z minulé úlohy): deklaraci proměnné pro uložení hodnoty vybrané klíčové položky, handlery pro kliknutí na záznam v tabulce a změnu ve widgetu pro vložení položky datum (oba jsou v proceduře initialize), procedury tf_RO, tf_RW a tableClick. U té se ještě chvíli zastavíme. Již v minulé kapitole jsme řešili, že načtení hodnot z tabulky má různý tvar (konkrétně se jedná o použití příkazů get nebo getValue). Problém souvisí s tím, jak máme deklarovanou tabulku a jednotlivé její sloupce. V minulé kapitole jsme je deklarovali pomocí typu Record, a proto bylo nutné použít variantu getValue(x). Nyní máme opět deklarováno jako String, a tak stačí pouze varianta get(x).

Vše je již připraveno a my si můžeme ukázat dva způsoby, jak se pomocí Hibernate dají mazat záznamy:

private void deleteRec1() {
        Short ID = pid.shortValue();

        SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
        Session session = sessionFactory.openSession();
        session.getTransaction().begin();                   //1

        Query query = session.createQuery("DELETE udaje WHERE id = :ID");   //2
        query.setParameter("ID",ID);                        //3
        Integer res = query.executeUpdate();
        session.getTransaction().commit();                  //4

        session.flush();
        sessionFactory.close(); }

První varianta je založena na jazyce HQL, o kterém již byla řeč. Některé příkazy jsou již známé, a proto budeme komentovat pouze ty vybrané:

  1. řádek – zde se poprvé v našem seriálu použijí transakce. Není na místě zde dlouze objasňovat, o co se jedná, takže jenom krátce: musí se zde použít, protože Hibernate má defaultně nastavenou hodnotu, která nepovoluje automatické odsouhlasení transakce (lze najít ve výpisu v konzole při běhu aplikace). Začínáme tím, že se transakce zahájí
  2. řádek – dotaz na vymazání vybraného záznamu, kde je použita formální proměnná (ID). Je třeba dát pozor na jednu věc: v některých návodech se objevuje formát dotazu DELETE FROM udaje WHERE id = :ID. Aktuálně fungují obě varianty bez problémů
  3. řádek – za formální parametr v dotazu se dosadí konkrétní hodnoty či proměnná
  4. řádek – potvrdí se transakce

Abychom mohli proceduru výkonnou vyzkoušet, vytvoříme velmi jednoduchou a známou volací proceduru:

private void onDelete() {
        deleteRec1();
        viewTable();
        tabPane.getSelectionModel().select(0); }

Pak už stačí tuto volací proceduru přiřadit k akci příslušného tlačítka a mazání vyzkoušet. Jako první ověříme funkci výběru záznamu z tabulky (č. 16) – viz třetí obrázek v galerii. Čtvrtý a pátý obrázek v galerii nám pak ukazují, že fungují obě varianty dotazu, jak bylo uvedeno v textu výše (vymazány záznamy č. 16 a 17). Ještě než přejdeme k další variantě mazání záznamů, doplníme krátkou informaci: bylo by samozřejmě možné implementovat smyčku pro zachycení výjimek a podle výsledku pak transakci buď odsouhlasit nebo odmítnout. Jenom pro jistotu ještě odkaz na PG manuál: Transactions.

Druhá varianta mazání záznamů bude využívat aplikační třídu, kterou máme vytvořenou. Kód bude ve své výkonné části zásadně odlišný:

private void deleteRec2() {
        SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
        Session session = sessionFactory.openSession();
        session.getTransaction().begin();

        udaje radek = new udaje();          //1
        radek.setId(pid.shortValue());          //2
        session.delete(radek);              //3
        session.getTransaction().commit();

        session.flush();
        sessionFactory.close(); }

Opět se musí použít transakce a komentářem opatříme pouze výkonnou část procedury:

  1. řádek – pod vybraným názvem se vytvoří nová instance aplikační třídy
  2. řádek – využije se v této třídě deklarovaný příslušný Setter a přiřadí se mu požadovaná hodnota
  3. řádek – provede se příkaz pro vymazání vybraného záznamu

Jedno důležité upozornění: v těchto případech je nutné použít příkaz

session.flush();

, abychom zajistili synchronizaci tabulkových záznamů a jejich mapovaných aplikačních objektů! Šestý obrázek galerie ukazuje, že je funkční i tato varianta a zmizel záznam č. 18. Kdo by se trochu více pátral, tak zjistí, že je možný ještě třetí způsob mazání záznamů. My ho zde ale nebudeme prezentovat, takže uvedeme pouze odkaz pro případné zájemce: Hibernate Delete Entity. Tímto odkazem ukončíme experimenty s mazáním záznamů a pustíme se do další kapitoly CRUD – aktualizace záznamů. Do naší ukázkové úlohy si překopírujeme proceduru rec_Update a jako u mazání si ukážeme dvě varianty. Nebudeme používat žádné kontroly obsahu editačních polí, změníme pouze dvě položky – celé číslo a velké desetinné a ke změně vybereme poslední záznam v tabulce s pořadovým číslem 28. Jako první opět varianta s HQL:

private void save_Update1() {
        SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
        Session session = sessionFactory.openSession();
        session.getTransaction().begin();

        Query query = session.createQuery("UPDATE udaje SET celecis= :celec, descis= :desec WHERE id = :ID");   //1
        query.setParameter("celec",Integer.valueOf(u_Cele.getText()));                      //2
        query.setParameter("desec",Double.valueOf(u_Desetinne.getText()));                  //3
        query.setParameter("ID",pid.shortValue());                              //4
        query.executeUpdate();                                          //5

        session.getTransaction().commit();
        session.flush();
        sessionFactory.close();

        viewTable();
        tf_RO();
        a_Button.setDisable(true);
        e_Button.setDisable(true);
        u_Button.setDisable(true);
        tabPane.getSelectionModel().select(0); }

Jak je z kódu zřejmé, jsou zde vlastně 4 různé sekce. První, třetí a čtvrtá jsou známé a očekávané, takže okomentujeme pouze tu druhou:

  1. řádek – zde je definován HQL dotaz se třemi formálními parametry pro ukládané hodnoty a hodnotu klíčové položky
  2. řádek – zadává se parametr pro první ukládanou položku pomocí obsahu editačního pole
  3. řádek – zadává se parametr pro druhou ukládanou položku pomocí obsahu editačního pole
  4. řádek – zadává se parametr klíčové položky. Pořadí těchto parametrů je vzhledem k jejich pojmenování libovolné
  5. řádek – provádí se dotaz se skutečnými parametry

Sedmý obrázek v galerii nám ukazuje, že funguje výběr zvoleného záznamu v tabulce. Předposlední obrázek pak dokazuje, že dvě změny v položkách se projevily i v tabulce. Proto můžeme klidně přejít ke druhé variantě aktualizace záznamů. Druhá procedura bude velmi podobná té první, a tak uvedeme pouze druhou sekci s výkonnými příkazy:

private void save_Update2() {
    ...
    udaje radek = (udaje) session.get(udaje.class, pid.shortValue());   //1
    radek.setCelecis(Integer.valueOf(u_Cele.getText()));            //2
    radek.setDescis(Double.valueOf(u_Desetinne.getText()));         //3
    session.update(radek);                          //4
    ... }
  1. řádek – pomocí názvu třídy a vlastnosti sezení se vybírá hodnota klíčové položky
  2. řádek – pomocí Setteru se do instance vkládá hodnota z editačního pole v příslušném formátu
  3. řádek – pomocí Setteru se do instance vkládá hodnota z editačního pole v příslušném formátu
  4. řádek – pomocí funkce sezení se nové hodnoty ukládají

Poslední obrázek v galerii dokládá, že i druhá změna obou položek proběhla bez problémů. Tímto konstatováním můžeme dnešní díl ukončit a na závěr si jako obvykle přidáme přílohu s kompletním kódem: samexam6.java.

V dnešním dílu jsme si ukázali zobrazení údajů v tabulce a po dvou možnostech mazání a aktualizace záznamů. V příštím dílu dokončíme kapitolu o Hibernate a začneme se věnovat možnostem využití jiných druhů databáze, lehce si nastíníme její výběr a ukážeme si vytvoření a základní administraci tabulek v databázi.

Našli jste v článku chybu?
Měšec.cz: Udali ho na nelegální software a přišla Policie

Udali ho na nelegální software a přišla Policie

DigiZone.cz: Výzkum FTV Prima. HbbTV má úspěch

Výzkum FTV Prima. HbbTV má úspěch

120na80.cz: Bonbon si schovejte na přistání

Bonbon si schovejte na přistání

Měšec.cz: Se stavebkem k soudu už (většinou) nemusíte

Se stavebkem k soudu už (většinou) nemusíte

Měšec.cz: Kurzy platebních karet: vyplatí se platit? (TEST)

Kurzy platebních karet: vyplatí se platit? (TEST)

Lupa.cz: Japonská invaze. Proč SoftBank kupuje ARM?

Japonská invaze. Proč SoftBank kupuje ARM?

Vitalia.cz: Jak na domácí zmrzlinu?

Jak na domácí zmrzlinu?

Podnikatel.cz: Daň z nemovitosti? Změny budou v říjnu

Daň z nemovitosti? Změny budou v říjnu

DigiZone.cz: Přechod na DVB-T2? Kolem miliardy...

Přechod na DVB-T2? Kolem miliardy...

Podnikatel.cz: Fotogalerie: Jesenka už má skoro 50 let

Fotogalerie: Jesenka už má skoro 50 let

Měšec.cz: Investice do drahých kovů - znáte základní chyby?

Investice do drahých kovů - znáte základní chyby?

120na80.cz: Jaké plavecké pomůcky vaše dítě ochrání?

Jaké plavecké pomůcky vaše dítě ochrání?

Podnikatel.cz: Místa, kde hází podnikání klacky pod nohy

Místa, kde hází podnikání klacky pod nohy

Lupa.cz: eIDAS: Nepřehnali jsme to s výjimkami?

eIDAS: Nepřehnali jsme to s výjimkami?

Lupa.cz: Vodafone umí volání přes Wi-Fi. Z ciziny jako v ČR

Vodafone umí volání přes Wi-Fi. Z ciziny jako v ČR

Vitalia.cz: Sobotní masakr žrádla, chlastu a zábavy

Sobotní masakr žrádla, chlastu a zábavy

Měšec.cz: Test: Výběry z bankomatů v cizině a kurzy

Test: Výběry z bankomatů v cizině a kurzy

Vitalia.cz: Petr Koukal: Až rakovina mi zkvalitnila život

Petr Koukal: Až rakovina mi zkvalitnila život

Vitalia.cz: Tohle je Břicháč Tom, co zhubnul 27 kg

Tohle je Břicháč Tom, co zhubnul 27 kg

Měšec.cz: Ceny PHM v Evropě. Finty na úspory

Ceny PHM v Evropě. Finty na úspory