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:

widgety

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?
Vitalia.cz: Tohle jsou nejlepší česká piva podle odborníků

Tohle jsou nejlepší česká piva podle odborníků

Měšec.cz: TEST: Vyzkoušeli jsme pražské taxikáře

TEST: Vyzkoušeli jsme pražské taxikáře

120na80.cz: Pálení žáhy: která jídla ne a co nás uzdraví?

Pálení žáhy: která jídla ne a co nás uzdraví?

Root.cz: Hořící telefon Samsung Note 7 zapálil auto

Hořící telefon Samsung Note 7 zapálil auto

Lupa.cz: Cimrman má hry na YouTube i vlastní doodle

Cimrman má hry na YouTube i vlastní doodle

DigiZone.cz: Nova opět stahuje „milionáře“

Nova opět stahuje „milionáře“

Podnikatel.cz: Byla finanční manažerka, teď cvičí jógu

Byla finanční manažerka, teď cvičí jógu

Vitalia.cz: Jsou vegani a vyrábějí nemléko

Jsou vegani a vyrábějí nemléko

Podnikatel.cz: Letáky? Lidi zuří, ale ony stále fungují

Letáky? Lidi zuří, ale ony stále fungují

DigiZone.cz: Parlamentní listy: kde končí PR...

Parlamentní listy: kde končí PR...

Vitalia.cz: Fyzioterapeutka: Chůze naboso? Rozhodně ano!

Fyzioterapeutka: Chůze naboso? Rozhodně ano!

DigiZone.cz: DVB-T2 ověřeno: seznam TV zveřejněn

DVB-T2 ověřeno: seznam TV zveřejněn

DigiZone.cz: Samsung EVO-S: novinka pro Skylink

Samsung EVO-S: novinka pro Skylink

DigiZone.cz: Numan Two: rozhlasový přijímač s CD

Numan Two: rozhlasový přijímač s CD

Vitalia.cz: Pryč se zastaralým stravováním ve školách

Pryč se zastaralým stravováním ve školách

Vitalia.cz: dTest odhalil ten nejlepší kečup

dTest odhalil ten nejlepší kečup

120na80.cz: Co je padesátkrát sladší než cukr?

Co je padesátkrát sladší než cukr?

DigiZone.cz: Světový pohár v přímém přenosu na ČT

Světový pohár v přímém přenosu na ČT

Vitalia.cz: 5 chyb, které děláme při skladování potravin

5 chyb, které děláme při skladování potravin

Podnikatel.cz: Tyto pojmy k #EET byste měli znát

Tyto pojmy k #EET byste měli znát