Body zdarma Body zdarma
Využij podzimních slev a získej od nás až 40 % bodů zdarma! Více zde

Lekce 3 - Databáze v Java JDBC - Výpis dat a parametry

Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem.
Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulém dílu našeho seriálu tutoriálů o databázích v Javě jsme si připravili databázi s testovacími daty. V dnešním dílu se k ní v Javě připojíme a budeme z ní číst hodnoty.

Vytvoření projektu

Vytvoříme si novou Java application s názvem Slovnicek. V oknu Projects na projekt klikneme pravým a zvolíme Properties.

V kategoriích vlevo vybereme Libraries a klikneme na Add Library.

Přidání MySQL ovladače do Java projektu

V dialogu vybereme MySQL JDBC Driver a potvrdíme. Když budete chtít pracovat s jakoukoli databází pomocí JDBC, je vždy třeba přidat tímto způsobem příslušný Connector. Ten se dá stáhnout jako soubor .jar na webu výrobce databáze a přidat stejným způsobem pomocí tlačítka Add JAR.

Přidání MySQL ovladače do Java projektu

Načtení ovladače

V minulosti bylo třeba JDBC ovladač před použitím načíst. Dělalo se to tímto způsobem:

try {
        Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException ex) {
        System.out.println("Chyba při načtení databázového ovladače");
}

Java si dnes již ovladač načte sama a tento kód tedy není třeba. Uvádím ho jen z toho důvodu, kdybyste ho viděli ve starších projektech nebo zastaralých tutoriálech.

Connection, PreparedStatement a ResultSet

S databází budeme pracovat pomocí svaté trojice tříd Connection, PreparedStatement a ResultSet. Všechny se nacházejí v balíčku java.sql.

Connection

Connection je databázové spojení. To je nutné vytvořit předtím, než se databáze na něco zeptáme. Při jeho vytváření uvedeme tzv. connection string. To je řetězec obsahující jméno databázového ovladače, URL serveru, kde databáze běží, dále název databáze, uživatelské jméno a heslo.

Vytvoření nové instance spojení bude v našem případě vypadat takto:

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

PreparedStatement

PreparedStatement je databázový dotaz. Při vytvoření instance zadáváme SQL kód, který chceme na databázi spustit. Java obsahuje také třídu Statement, která se od PreparedStatement liší tím, že nemůže obsahovat parametry (o tom dále).

Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

V našem případě si vytvoříme následující instanci dotazu:

PreparedStatement dotaz = spojeni.prepareStatement("SELECT * FROM slovo")

Všimněte si, že se dotaz vytváří přes instanci spojení.

Slušelo by se vysvětlit i samotný SQL dotaz. Příkazem SELECT říkáme, že chceme z databázové tabulky vybrat data. Hvězdička označuje, že u výsledných řádků chceme vybrat hodnoty ze všech sloupců. FROM slovo označuje, že vybíráme z tabulky slovo. Dotaz tedy vybere všechny hodnoty všech slov.

ResultSet

ResultSet je kolekce výsledků, které vrátil nějaký SQL dotaz. ResultSet naplněný výsledky SQL příkazu SELECT získáme pomocí metody executeQuery() na instanci dotazu.

ResultSet vysledky = dotaz.executeQuery();

Uzavírání spojení

Pokud jste v Javě již pracovali se soubory, nebude pro vás žádným překvapením, že i databázové spojení se musí uzavřít. Malým překvapením však může být, že se musíme postarat o správné uzavření všech 3 databázových objektů. Kdybychom to neudělali, zůstalo by spojení otevřené a server by se za nějakou dobu zahltil.

Nejjednodušší způsob je vytvořit objekty v bloku try-with-resources (TWR). Jakmile Java tento blok kódu opustí, sama se postará o uzavření instancí, které jsou v deklaraci bloku vytvořené.

try (Connection spojeni = DriverManager.getConnection("jdbc:mysql://localhost/slovnicek_db?user=root&password=");
        PreparedStatement dotaz = spojeni.prepareStatement("SELECT * FROM slovo");
        ResultSet vysledky = dotaz.executeQuery();) {

} catch (SQLException ex) {
        System.out.println("Chyba při komunikaci s databází");
}

Pokud se něco pokazí, informujeme o tom uživatele chybovou hláškou. Při ladění blok catch zakomentujte, abyste mohli na chyby reagovat a kód opravit.

Výpis výsledků

V proměnné vysledky máme již načtená slovíčka z databáze. Zbývá je jen vypsat. ResultSet na sobě obsahuje metodu next(). Ta přesune aktuální pozici v kolekci na další prvek nebo vrátí false v případě, že jsme na konci výsledků. Next je nutné zavolat minimálně jednou, pokud chceme z výsledků něco číst.

K samotnému čtení hodnoty na aktuálním řádku výsledků slouží metody začínající get. Nalezneme zde getInt(), getString(), getDate() a další. Metodám můžeme dát jako parametr buď název sloupce nebo jeho číselný index. U číselného indexu pozor, první sloupec má index 1.

while (vysledky.next()) {
        int id = vysledky.getInt(1);
        String cesky = vysledky.getString("cesky");
        String anglicky = vysledky.getString("anglicky");
        System.out.println("Id: " + id + ", česky: " + cesky + ", anglicky: " + anglicky);
}

Kódem výše iterujeme nad výsledky, získáváme jejich parametry a ty poté vypisujeme do konzole. Ukažme si ještě kompletní kód aplikace:

try (Connection spojeni = DriverManager.getConnection("jdbc:mysql://localhost/slovnicek_db?user=root&password=a");
        PreparedStatement dotaz = spojeni.prepareStatement("SELECT * FROM slovo");
        ResultSet vysledky = dotaz.executeQuery();) {

        while (vysledky.next()) {
                int id = vysledky.getInt(1);
                String cesky = vysledky.getString("cesky");
                String anglicky = vysledky.getString("anglicky");
                System.out.println("Id: " + id + ", česky: " + cesky + ", anglicky: " + anglicky);
        }

} catch (SQLException ex) {
        System.out.println("Chyba při komunikaci s databází");
}

Můžete si zkusit, že aplikace opravdu vypíše všechna slovíčka z databáze:

Čtení z MySQL databáze pomocí JDBC v Javě

Předávání parametrů

Doveďme aplikaci opravdu do podoby slovníčku. Uživatele necháme zadat slovíčko v angličtině a to mu následně přeložíme do češtiny.

SQL injekce

SQL dotaz by měl nyní vybrat jen určitý řádek, je do něj tedy třeba dodat podmínky. Toho docílíme pomocí klauzule WHERE. Naivně bychom mohli vložit slovíčko od uživatele přímo do těla SQL dotazu:

// Tento kód je nebezpečný
Scanner scanner = new Scanner(System.in, "Windows-1250");
System.out.println("Zadej anglické slovíčko k překladu:");
String anglicky = scanner.nextLine();
try (Connection spojeni = DriverManager.getConnection("jdbc:mysql://localhost/slovnicek_db?user=root&password=");
        PreparedStatement dotaz = spojeni.prepareStatement("SELECT cesky FROM slovo WHERE anglicky=\"" + anglicky + "\"");
        ResultSet vysledky = dotaz.executeQuery();) {

        vysledky.next();
        String cesky = vysledky.getString("cesky");
        System.out.println("Překlad " + anglicky + ": " + cesky);
} catch (SQLException ex) {
        System.out.println("Chyba při komunikaci s databází");
}

Výsledek:

MySQL slovníček v Javě

Kód se příliš nezměnil. V SQL dotazu již nevybíráme všechny sloupce, ale pouze sloupec cesky. Kromě toho nám zde přibyla podmínka WHERE. Výsledky již nenačítáme ve while cyklu, jelikož nás zajímá pouze jeden.

Ačkoli se zdá, že aplikace funguje perfektně, opak je pravdou. Cokoli uživatel zadá se vloží přímo do SQL dotazu. Co se stane, když zadá např. následující řetězec?

"; DROP TABLE slovo --

Na databázi se spustí příkaz k vymazání tabulky a máme po datech. To je ještě ten lepší případ, šikovnější uživatel by nám přes slovníček mohl třeba vytahat hesla uživatelů z jiné tabulky. A to už by byl problém. Věřte nebo ne, ale uživatelé takové vstupy opravdu zadávají a vaše aplikace se jim musí bránit. Této technice útoku se říká SQL injection, protože se vkládá cizí SQL kód do našeho dotazu.

Předávání parametrů

Celý problém je samozřejmě v tom, že vkládáme vstup od uživatele přímo do SQL dotazu. Jelikož nikdy nemůžeme vědět, jestli se v nějaké proměnné může vyskytnout něco, co uživatel zadal, upravme náš problém na: vložení kterékoli proměnné do SQL dotazu je velké bezpečnostní riziko. V minulosti se proměnné ošetřovaly speciální funkcí, která tzv. zescapovala nebezpečné znaky (zejména uvozovky). Nejbezpečnější je ovšem používat tzv. prepared statements.

Prepared statement je dotaz, který obsahuje místo parametrů zástupné znaky, nejčastěji otazníky. Samotné hodnoty se do dotazu dosadí odděleně a sama databáze se postará o jejich bezpečné vložení do dotazu.

Přepišme naší aplikaci tak, aby používala parametrizované dotazy:

Scanner scanner = new Scanner(System.in, "Windows-1250");
System.out.println("Zadej anglické slovíčko k překladu:");
String anglicky = scanner.nextLine();
try (Connection spojeni = DriverManager.getConnection("jdbc:mysql://localhost/slovnicek_db?user=root&password=");
        PreparedStatement dotaz = spojeni.prepareStatement("SELECT cesky FROM slovo WHERE anglicky=?");) {
        dotaz.setString(1, anglicky);
        try (ResultSet vysledky = dotaz.executeQuery()) {
                vysledky.next();
                String cesky = vysledky.getString("cesky");
                System.out.println("Překlad " + anglicky + ": " + cesky);
        }
} catch (SQLException ex) {
        System.out.println("Chyba při komunikaci s databází");
}

Všimněte si otazníku v dotazu a volání metody setString(), která nastaví první parametr v dotazu na daný řetězec. Samozřejmě zde nalezneme i metody pro další datové typy. Naše aplikace je nyní bezpečná.

Příště si ukážeme jak záznamy v databázi editovat. Dnešní projekt je se zdrojovým kódem jako vždy ke stažení níže.


 

Stáhnout

Staženo 527x (18.28 kB)
Aplikace je včetně zdrojových kódů v jazyce java

 

 

Článek pro vás napsal David Čápka
Avatar
Jak se ti líbí článek?
13 hlasů
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor sítě se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.
Předchozí článek
Návrh MySQL databáze v NetBeans IDE
Všechny články v sekci
Databáze v Javě - JDBC
Miniatura
Následující článek
Databáze v Java JDBC - INSERT, UPDATE, DELETE a COUNT
Aktivity (1)

 

 

Komentáře
Zobrazit starší komentáře (11)

Avatar
Filip Něnička:8. října 16:58

Ahoj všem.
Poprosil bych vás o radu. Bohužel se mi nedaří vypsat data z databáze a už nevím čím to může být. Databázi mám připojenou podle lekce 2, to funguje bez problémů. Když dám execute command a něco zadám tak to funguje, takže propojené to je. Když se ale snažím vypsat databázi dle této lekce tak to nefunguje a hodí to "Chyba při komunikaci s databází". Nevíte kde by mohl být problém? Rozdíl jsem našel v JDK verzi(mám JDK 13) a kihovnu jsem si musel stáhnout a vytvořit sám. Nebyla tam.

Můj kód:

package slovnicek;
import java.sql.Connec­tion;
import java.sql.Driver­Manager;
import java.sql.Prepa­redStatement;
import java.sql.ResultSet;
import java.sql.SQLEx­ception;

public class Slovnicek {
public static void main(String[] args) {
try (Connection spojeni = DriverManager­.getConnection("jdbc:mys­ql://localhos­t:3306/slovni­cek_db?user=ro­ot&password=");
PreparedStatement dotaz = spojeni.prepa­reStatement("SE­LECT * FROM slovo");
ResultSet vysledky = dotaz.execute­Query();) {

while (vysledky.next()) {
int id = vysledky.getInt(1);
String cesky = vysledky.getStrin­g("cesky");
String anglicky = vysledky.getStrin­g("anglicky");
System.out.prin­tln("Id: " + id + ", česky: " + cesky + ", anglicky: " + anglicky);
}

} catch (SQLException ex) {
System.out.prin­tln("Chyba při komunikaci s databází");
}

}
}

Díky za rady

Editováno 8. října 17:00
 
Odpovědět 8. října 16:58
Avatar
Petr Štechmüller
Překladatel
Avatar
Odpovídá na Filip Něnička
Petr Štechmüller:8. října 18:41

Ahoj, dej si do té výjimky výpis chyby

ex.printStackTrace();

To to to řekne, kde přesně je chyba ;-)

Odpovědět 8. října 18:41
Pokud spolu kód a komentář nekorespondují, budou patrně oba chybné
Avatar
Robert Michalovič:9. října 6:25

Důležitou roli hraje taky i kompatibilita mezi verze connectoru(stažený *.jar) a verzí databáze a verzí Javy. Podle mě je problém v JDK13. Dost pochybuji, že je podporována. Používej 8čku a stáhni si správný connector JDBC.
viz. tabulka

 
Odpovědět 9. října 6:25
Avatar
Odpovídá na Robert Michalovič
Filip Něnička:9. října 19:05

Ahoj Roberte.
Díky za radu, pomohlo to :)

Filip

 
Odpovědět 9. října 19:05
Avatar
Filip Něnička:13. října 16:43

Ahoj,
měl bych ještě jeden dotaz. Snažím se změnit název tabulky co mám v databázi. Nemohu příjít na to, proč mi to nefunguje přes setName. Nevíte někdo kde tam je chyba?

Díky

Filip

String nazevVozidla = "fdadfa";
try (Connection spojeni = DriverManager­.getConnection("jdbc:mys­ql://localhos­t/drivvo?user=ro­ot&password=");
PreparedStatement dotaz = spojeni.prepa­reStatement("RE­NAME TABLE tabulka to ?");) {
dotaz.setString(1, nazevVozidla);
int radku = dotaz.execute­Update();
System.out.prin­tln(radku);
} catch (SQLException ex) {
ex.printStackTra­ce();
System.out.prin­tln("Chyba při komunikaci s databází");

 
Odpovědět 13. října 16:43
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Petr Štechmüller
Překladatel
Avatar
Odpovídá na Filip Něnička
Petr Štechmüller:13. října 17:01

Ahoj, pro vložení kódu tu máme tlačítko.

Můžu se zeptat, odkud jsi získal ten dotaz na přejmenování tabulky? :-) Já jsem našel něco jiného:

ALTER TABLE table_name
  RENAME TO new_table_name
Odpovědět 13. října 17:01
Pokud spolu kód a komentář nekorespondují, budou patrně oba chybné
Avatar
Odpovídá na Petr Štechmüller
Filip Něnička:13. října 18:05

Ahoj,
našel jsem to myslím na jednom blogu. Pokud zde můžu vkládat odkazy tak bych ho sem vložil. Ono to funguje pokud tam místo otazníku zadám jméno. Jde o ten otázník a setString. To nefunguje. To je ten problém :-/

 
Odpovědět 13. října 18:05
Avatar
Petr Štechmüller
Překladatel
Avatar
Odpovídá na Filip Něnička
Petr Štechmüller:13. října 19:35

Co přesně znamená nefunguje? Je to dost obecný popis problému, se kterým ti nemůžu poradit. Koukal jsem, že tam máš při výjimce výpis ze stack trace, tak ho sem hoď

Odkaz sem samozřejmě hodit můžeš ;-)

Odpovědět  +1 13. října 19:35
Pokud spolu kód a komentář nekorespondují, budou patrně oba chybné
Avatar
Filip Něnička:14. října 18:52

Ahoj.
Díky za trpělivost :-)

Háže mi to tuto chybu"
java.sql.SQLSyn­taxErrorExcep­tion: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near fdadfa at line 1

A tady je ten blog :-)

"":https://blog.marceloaltmann.com/…as-no-mysql/

 
Odpovědět 14. října 18:52
Avatar
Petr Štechmüller
Překladatel
Avatar
Odpovídá na Filip Něnička
Petr Štechmüller:14. října 19:19

Tak mám pro tebe novniku: to co chceš, nelze aplikovat. Metody setString() + další takové slouží pouze pro nastavování hodnot do sloupečků.

Zde je o tom delší povídání.

You can't. You need to contruct the sql with string concatenation/pla­ceholder with String.format. prepared statement is for the column values not for table name.

Odpovědět  +1 14. října 19:19
Pokud spolu kód a komentář nekorespondují, budou patrně oba chybné
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 21. Zobrazit vše