IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

3. diel - Databázy v Java JDBC - Výpis dát a parametre

V minulom dieli nášho seriálu, Návrh MySQL databázy v IntelliJ IDE, sme si pripravili databázu s testovacími dátami.

V dnešnom tutoriáli sa k našej MySQL databáze v Jave pripojíme a budeme z nej čítať hodnoty.

Inštalácia MySQL connectora

Pre pripojenie k MySQL databáze v našom Java projekte využijeme MySQL connector. V IntelliJ IDE si náš projekt otvoríme a do súboru pom.xml, dovnútra tagu <dependencies>, pridáme nasledujúcu závislosť:

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>9.0.0</version>
</dependency>

Po vložení sa nám v pravom hornom rohu objaví ikonka na načítanie zmien, ktoré sme práve vykonali (pridanie závislosti). Na ikonku klikneme:

Pridanie závislosti do súboru pom.xml. - Databázy v Jave - JDBC

Tým sme úspešne pridali závislosť potrebnú na pripojenie k MySQL databáze 🙂

Načítanie ovládača

V minulosti bolo treba JDBC ovládač pred použitím načítať. Robilo sa to týmto spôsobom:

try {
    Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException ex) {
    System.out.println("Error while loading the database driver");
}

Java si dnes už ovládač načíta sama a tento kód teda nie je potrebný. Uvádzame ho len preto, že je niekedy ešte vidieť v starších projektoch alebo zastaraných tutoriáloch.

Triedy pre prácu s databázou

S databázou budeme pracovať v metóde main() pomocou svätej trojice tried Connection, PreparedStatement a ResultSet. Všetky sa nachádzajú v balíčku java.sql. Do projektu si teda doplníme import:

import java.sql.*;

Trieda Connection

Connection zaisťuje databázové spojenie. To je potrebné vytvoriť predtým, než sa databáza na niečo spýtame. Pri jeho vytváraní uvedieme tzv. connection string. Ide o reťazec obsahujúci meno databázového ovládača, URL servera, kde databáza beží, ďalej názov databázy, užívateľské meno a heslo.

Vytvorenie novej inštancie spojenia bude v našom prípade vyzerať takto:

Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/dictionary_db?user=root&password=")

Trieda PreparedStatement

PreparedStatement je databázový dotaz. Pri vytvorení inštancie zadávame SQL kód, ktorý chceme na databáze spustiť. Java obsahuje aj triedu Statement, ktorá sa od PreparedStatement líši tým, že nemôže obsahovať parametre.

V našom prípade si vytvoríme nasledujúcu inštanciu dotazu:

PreparedStatement statement = connection.prepareStatement("SELECT * FROM word")

Vytvorili sme jednoduchý SQL dotaz. Príkazom SELECT hovoríme, že chceme z databázovej tabuľky vybrať dáta. Hviezdička označuje, že pri výsledných riadkoch chceme vybrať hodnoty zo všetkých stĺpcov. FROM word označuje, že vyberáme z tabuľky word. Dotaz teda vyberie všetky hodnoty všetkých slov.

Všimnime si, že sa otázka vytvára cez inštanciu spojenia.

Trieda ResultSet

ResultSet je kolekcia výsledkov, ktoré vrátil nejaký SQL dotaz. ResultSet naplnený výsledkami SQL príkazu SELECT získame pomocou metódy executeQuery() na inštancii dotazu:

ResultSet results = statement.executeQuery();

Uzatváranie spojenia

Pokiaľ ste v Jave už pracovali so súbormi, nebude pre vás žiadnym prekvapením, že aj databázové spojenie sa musí uzavrieť. Malým prekvapením však môže byť, že sa musíme postarať o správne uzavretie všetkých troch databázových objektov. Keby sme to neurobili, zostalo by spojenie otvorené a server by sa za nejaký čas zahltil.

Najjednoduchší spôsob je vytvoriť objekty v bloku try-with-resources. Akonáhle Java tento blok kódu opustí, sama sa postará o uzavretie inštancií, ktoré sú v deklarácii bloku vytvorené:

try (Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/dictionary_db?user=root&password=");
    PreparedStatement statement = connection.prepareStatement("SELECT * FROM word");
    ResultSet results = statement.executeQuery();) {

    // Processing the result

} catch (SQLException ex) {
    System.out.println("Error while communicating with the database");
}

Pokiaľ sa niečo pokazí, informujeme o tom užívateľa chybovou hláškou.

Pri ladení blok catch zakomentujeme, aby sme mohli na chyby reagovať a kód opraviť.

Výpis výsledkov

V premennej results máme už načítané slovíčka z databázy. Zostáva ich len vypísať. ResultSet na to poskytuje metódu next(). Tá presunie aktuálnu pozíciu v kolekcii na ďalší prvok alebo vráti false v prípade, že sme na konci výsledkov.

Na samotné čítanie hodnoty na aktuálnom riadku výsledkov slúžia metódy getInt(), getString(), getDate() a ďalšie. Metódam môžeme dať ako parameter buď názov stĺpca alebo jeho číselný index. Pri číselnom indexe pozor, prvý stĺpec má index 1:

while (results.next()) {
    int id = results.getInt(1);
    String english = results.getString("english");
    String spanish = results.getString("spanish");
    System.out.println("Id: " + id + ", English: " + english + ", Spanish: " + spanish);
}

Kódom vyššie iterujeme nad výsledkami, získavame ich parametre a tie potom vypisujeme do konzoly. Ukážme si ešte kompletný kód aplikácie:

try (Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/dictionary_db?user=root&password=");
    PreparedStatement statement = connection.prepareStatement("SELECT * FROM word");
    ResultSet results = statement.executeQuery()) {

    while (results.next()) {
        int id = results.getInt(1);
        String english = results.getString("english");
        String spanish = results.getString("spanish");
        System.out.println("Id: " + id + ", English: " + english + ", Spanish: " + spanish);
    }

} catch (SQLException ex) {
    System.out.println("Error while communicating with the database");
}

Môžeme si skúsiť, že aplikácia naozaj vypíše všetky slovíčka z databázy:

Konzolová aplikácia
Id: 1, English: computer, Spanish: ordenador
Id: 2, English: ball, Spanish: pelota
Id: 3, English: dog, Spanish: perro
Id: 4, English: I, Spanish: yo
Id: 5, English: love, Spanish: amor
Id: 6, English: ICT, Spanish: ICT

Odovzdávanie parametrov

Doveďme aplikáciu naozaj do podoby slovníčka. Užívateľa necháme zadať slovíčko v španielčine a to mu následne preložíme do angličtiny.

SQL injekcia

SQL dotaz by mal teraz vybrať len určitý riadok, je do neho teda potrebné dodať podmienky. To docielime pomocou klauzuly WHERE. Naivne by sme mohli vložiť slovíčko od užívateľa priamo do tela SQL dotazu:

// This code is dangerous
Scanner scanner = new Scanner(System.in);
System.out.println("Enter a Spanish word to be translated:");
String spanish = scanner.nextLine();

try (Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/dictionary_db?user=root&password=");
    PreparedStatement statement = connection.prepareStatement("SELECT english FROM word WHERE spanish=\"" + spanish + "\"");
    ResultSet results = statement.executeQuery();) {

    results.next();
    String english = results.getString("english");
    System.out.println("Translating " + spanish + ": " + english);
} catch (SQLException ex) {
    System.out.println("Error while communicating with the database");
}

Výsledok:

Konzolová aplikácia
Enter a Spanish word to be translated:
ordenador
Translating ordenador: computer

Kód sa príliš nezmenil. V SQL dotaze už nevyberáme všetky stĺpce, ale iba stĺpec english. Okrem toho nám tu pribudla podmienka WHERE. Výsledky už nenačítame vo while cykle, pretože nás zaujíma iba jeden.

Hoci sa zdá, že aplikácia funguje perfektne, opak je pravdou. Čokoľvek užívateľ zadá, sa vloží priamo do SQL dotazu. Čo sa stane, keď zadá napr. nasledujúci reťazec:

"; DROP TABLE word --

Na databáze sa spustí príkaz na vymazanie tabuľky a máme po dátach. To je ešte ten lepší prípad, šikovnejší používateľ by nám cez slovníček mohol napríklad vyťahať heslá používateľov z inej tabuľky. A to už by bol problém. Verme alebo nie, ale používatelia takéto vstupy naozaj zadávajú a naše aplikácie sa im musia brániť. Tejto technike útoku sa hovorí SQL injection, pretože sa vkladá cudzí SQL kód do nášho dotazu.

Odovzdávanie parametrov

Celý problém je samozrejme v tom, že vkladáme vstup od používateľa priamo do SQL dotazu. V minulosti sa premenné ošetrovali špeciálnou funkciou, ktorá tzv. zoscapovala nebezpečné znaky (najmä úvodzovky). Najbezpečnejšie je však používať PreparedStatements. Ide o dotaz, ktorý obsahuje namiesto parametrov zástupné znaky, najčastejšie otázniky. Samotné hodnoty sa do dotazu dosadia oddelene a sama databáza sa postará o ich bezpečné vloženie do dotazu.

Prepíšme našu aplikáciu tak, aby používala parametrizované otázky:

Scanner scanner = new Scanner(System.in);
System.out.println("Enter a Spanish word to be translated:");
String spanish = scanner.nextLine();

try (Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/dictionary_db?user=root&password=");
    PreparedStatement statement = connection.prepareStatement("SELECT english FROM word WHERE spanish=?");) {

    statement.setString(1, spanish);

    try (ResultSet results = statement.executeQuery()) {
        results.next();
        String english = results.getString("english");
        System.out.println("Translating " + spanish + ": " + english);
    }

} catch (SQLException ex) {
    System.out.println("Error while communicating with the database");
}

Všimnime si otáznik v dotaze a volaní metódy setString(), ktorá nastaví prvý parameter v dotaze na daný reťazec. Samozrejme tu nájdeme aj metódy pre ďalšie dátové typy.

Výstup bude podobný:

Konzolová aplikácia
Enter a Spanish word to be translated:
perro
Translating perro: dog

Naša aplikácia je však oproti predchádzajúcemu príkladu teraz bezpečná.

V nasledujúcom kvíze, Kvíz - Základy práce s databázou v Java JDBC, si vyskúšame nadobudnuté skúsenosti z predchádzajúcich lekcií.


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 15x (2.1 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Java

 

Predchádzajúci článok
Návrh MySQL databázy v IntelliJ IDE
Všetky články v sekcii
Databázy v Jave - JDBC
Preskočiť článok
(neodporúčame)
Kvíz - Základy práce s databázou v Java JDBC
Článok pre vás napísal David Hartinger
Avatar
Užívateľské hodnotenie:
3 hlasov
David je zakladatelem ITnetwork a programování se profesionálně věnuje 15 let. Má rád Nirvanu, nemovitosti a svobodu podnikání.
Unicorn university David sa informačné technológie naučil na Unicorn University - prestížnej súkromnej vysokej škole IT a ekonómie.
Aktivity