Hlavní navigace

Jak pracovat efektivně s relační databází pomocí JDBC jazyka Java?

11. 11. 2018 21:29 (aktualizováno) Pavel Ponec

Pokud pro implementaci databázových dotazů nechcete použít žádný z mnoha javových ORM frameworků a přitom máte dojem, že ani springový nástroj JdbcTemplate není pro vás úplně to pravé, zkuste třídu JdbcBuilder z projektu UjoTools.

Každý, kdo někdy programoval SQL dotazy prostřednictvím knihovny JDBC zřejmě uzná, že nejde zrovna o šťastně navržené programové rozhraní. Možná právě proto vznikla celá řada knihoven, které se liší jak výčtem poskytovaných služeb, tak i mírou složitosti. Rád bych vám představil v tomto článku jednoduchou třídu z Java knihovny UjoTools s názvem JdbcBuilder. Jejím cílem je nabídnout pomoc při sestavování a vykonávání SQL příkazů, nic více ani méně. Třída JdbcBuilder tedy neřeší mapování výsledků na JavaBeans, neřeší optimalizace, ani neposkytuje databázové spojení. Součástí jUnitových testů je pár příkladů použití, některé si tady popíšeme.

Začnu použitím SQL příkazu SELECT:

JdbcBuilder sql = new JdbcBuilder()
    .write("SELECT")
    .column("t.id")
    .column("t.name")
    .write("FROM testTable t WHERE")
    .andCondition("t.name", "=", "A name")
    .andCondition("t.created", ">=", someDate);

for (ResultSet rs : sql.executeSelect(dbConnection)) {
    int id = rs.getInt(1);
    String name = rs.getString(2);
}

Třída JdbcBuilder si ukládá interně fragmenty SQL příkazu a eviduje použité parametry (konstanty). Na vyžádání obě tyto skupiny spojí a vrátí výsledek ve formátu PreparedStatement. Metoda write() vkládá libovolný text oddělený počáteční mezerou. Každá elementární podmínka (s parametrem dotazu) se vkládá samostatnou metodou. Metoda toString() vrací náhled SQL dotazu včetně připojených parametrů, takový výsledek však není vhodný pro zpracování v JDBC, k tomu slouží metoda getSql(). Výsledek příkazu SELECT lze získat alternativně voláním metody executeSelect(), výsledný Iterátor lze použít třeba v programové smyčce for(). Pokud projdeme celou kolekci výsledků do konce, tak Iterátor uzavře všechny své interní zdroje (netýká se to databázového spojení). Na produkčním prostředí však není dobré spoléhat na automatické zavírání (metodu close()), protože nepokrývá případy předčasného ukončení cyklu vlivem aplikační logiky nebo případné výjimky.

Druhá ukázka představuje použití SQL příkazu INSERT:

JdbcBuilder sql = new JdbcBuilder()
    .write("INSERT INTO testTable (")
    .columnInsert("id", 10)
    .columnInsert("name", "A name")
    .columnInsert("created", someDate)
    .write(")");

sql.executeUpdate(dbConnection);

Tady vidíme podobný koncept vkládání parametru společně s názvem databázového sloupce. Žádná z exekutivních metod nevolá commit() databázového spojení explicitně.

Poslední ukázka představuje použití SQL příkazu UPDATE:

JdbcBuilder sql = new JdbcBuilder()
    .write("UPDATE testTable SET")
    .columnUpdate("created", someDate.plusDays(1))
    .write("WHERE")
    .andCondition("id", "IN", 10, 20, 30)
    .andCondition("created = (?)", null, someDate)
    .andCondition("name", "IS NOT NULL", null);

sql.executeUpdate(dbConnection);

Použití příkazu DELETE je analogické. Pokud potřebujeme použít nějakou funkci jazyka SQL, tak napíšeme celou SQL podmínku do prvního argumentu s tím, že pozici SQL parametru označíme znakem otazníku (?). Druhý parametr metody pak dostane nedefinovanou hodnotu null a poslední argument zůstane beze změny. Pokud navržené API stále nevyhovuje, je možné doplnit vlastní metodu do potomka třídy JdbcBuilder, která získá přístup ke všem interním atributům.

Všechny ukázky lze odkrokovat v jUnitovém testu JdbcBuilderOriginalTest. Při psaní tohoto článku jsem však měl dojem, že se v kódu opakují zbytečně klíčová slova jazyka SQL a tak jsem takové výrazy přesunul do samostatného interface Sql jako konstanty. Ukázka s konstantami jazyka SQL je odolnější proti některým překlepům, zabránit nevalidní syntaxi však nedokáže. Náhled upraveného řešení lze shlédnout ve třídě JdbcBuilderTest:

JdbcBuilder sql = new JdbcBuilder()
    .write(Sql.UPDATE)
    .write("testTable")
    .write(Sql.SET)
    .columnUpdate("created", someDate.plusDays(1))
    .write(Sql.WHERE)
    .andCondition("id", Sql.IN, 10, 20, 30)

Co dodat závěrem? Třídu JdbcBuilder lze získat použitím závislosti na Maven artefaktu ujo-tools. Aktuálně je k dispozici verze 1.87 s velikostí 37kB. V nadcházející verzi se připravuje obecná podpora podmínek s více argumenty, aktuálně je podporovaná pouze fráze typu IN (?, …).

<dependency>
    <groupId>org.ujorm</groupId>
    <artifactId>ujo-tools</artifactId>
    <version>1.87</version>
</dependency>

Autor tohoto článku je i autorem ORM frameworku Ujorm postaveného na objektech typu KEY – VALUE. Zmíněný modul však závislost na takových objektech nevyžaduje a nevyžaduje ani závislost na žádném jiném Maven modulu.

Internetové odkazy

  1. JavaDoc třídy JdbcBuilder
  2. Blog frameworku Ujorm v češtině
  3. Domovská stránka Ujorm v angličtině
  4. Využijte GitHub pro konstruktivní návrhy