Java na webovém serveru: práce s databází II

V předchozím díle jsme začali téma práce s databází, naučili jsme se k ní přistupovat pomocí JSP značek a napsat si vlastní zjednodušenou DAO vrstvu. Dnes se budeme věnovat dvěma pokročilejším způsobům přístupu k databázi – použití třídy JdbcTemplate a ORM Hibernate.

Seriál: Java na webovém serveru (16 dílů)

  1. Java na serveru: úvod 8.1.2010
  2. Java na webovém serveru: první web 15.1.2010
  3. Java na webovém serveru: práce s databází 29.1.2010
  4. Java na webovém serveru: práce s databází II 12.2.2010
  5. Java na webovém serveru: lokalizace a formátování 19.2.2010
  6. Java na webovém serveru: autorizace a autentizace 26.2.2010
  7. Java na webovém serveru: autorizace a autentizace II 5.3.2010
  8. Java na webovém serveru: porovnání Javy a PHP 10.3.2010
  9. Java na webovém serveru: Vlastní JSP značky a servlety 17.3.2010
  10. Java na webovém serveru: posílání e-mailů a CAPTCHA 24.3.2010
  11. Java na webovém serveru: píšeme REST API 7.4.2010
  12. Java na webovém serveru: SOAP webové služby 14.4.2010
  13. Java na webovém serveru: hlasování a grafy v SVG 28.4.2010
  14. Java na webovém serveru: Komentáře a integrace s Texy 9.6.2010
  15. Java na webovém serveru: AJAX formuláře 23.6.2010
  16. Java na webovém serveru: implementujeme Jabber 30.6.2010

Jak jste si asi všimli v minulém díle, psát datovou vrstvu jen s použitím základního JDBC vyžaduje poměrně dost nudného a opakujícího se kódu. To znamená jednak práci navíc a jednak potenciální chybovost – čím víc kódu, tím víc míst, kde jsme mohli udělat chybu. Tyto problémy samozřejmě postupem času řešíme, optimalizujeme, vyčleňujeme opakující se kód do znovupoužitelných tříd… až najednou zjistíme, že si píšeme vlastní framework. Někdy je to správná cesta, jindy je ale lepší, soustředit se na jádro naší aplikace (obchodní logiku) a pro datovou vrstvu použít raději už hotový framework. V následujícím textu si proto ukážeme, jak využít kód, který za nás napsal někdo jiný – Hibernate a Spring.

Jen pro připomenutí: jako obvykle si z Mercurialu stáhneme aktuální verzi zdrojových kódů k dnešnímu dílu seriálu:

$ hg pull
$ hg up "4. díl"

Případně si je můžete stáhnout jako bzip2 archiv přes web.

Pomocník JdbcTemplate

Třída JdbcTemplate pochází z frameworku Spring. Jedná se o velmi rozsáhlý framework a JdbcTemplate představuje jen zlomek jeho možností. Základní kostru třídy PodnikDAO necháme stejnou a upravovat budeme jen vnitřek metod getPodniky() a ulozPodnik(). Prezentační vrstva JSP a JavaBean tak může zůstat nezměněná.

Do webového projektu si přidáme knihovnu Spring Framework (již obsažena ve vaší instalaci Netbeans):

Java na webovém serveru 4

Díky použití JdbcTemplate dojde k výrazné úspoře kódu (přinejmenším na první pohled). Z původních devatenácti řádků:

public Collection<Podnik> getPodniky() {
    Connection db = getSpojeni();
    PreparedStatement ps = null;
    ResultSet rs = null;
    try {
        ps = db.prepareStatement(getSQL(SQL.SELECT_VSECHNY));
        rs = ps.executeQuery();
        Collection<Podnik> vysledek = new ArrayList<Podnik>();

        while (rs.next()) {
            vysledek.add(new Podnik(rs.getInt("id"), rs.getString("nazev")));
        }

        return vysledek;
    } catch (Exception e) {
        log.log(Level.SEVERE, "Chyba při získávání podniků.", e);
        return null;
    } finally {
        zavri(db, ps, rs);
    }
}

Na pouhý jeden:

public Collection<Podnik> getPodniky() {
    return jdbcTemplate.query(getSQL(SQL.SELECT_VSECHNY), podnikRowMapper);
}

Řekněte, není to skvělé? Je to skvělé! …bohužel tady nejsme v teleshoppingu, takže nebudu zastírat i tu druhou stranu mince. Jak tedy k úspoře kódu došlo?

Mapování pomocí RowMapperu

Jistě jste si všimli proměnné podnikRowMapper  – ta odkazuje na instanci třídy PodnikRowMapper, kterou jsme si museli napsat. Třída implementuje springové rozhraní ParameterizedRowMapper a vypadá následovně:

…
public class PodnikRowMapper implements ParameterizedRowMapper<Podnik> {
    public Podnik mapRow(ResultSet rs, int i) throws SQLException {
        Podnik p = new Podnik();
        p.setId(rs.getInt("id"));
        p.setNazev(rs.getString("nazev"));
        return p;
    }
}

RowMapper se stará o vytažení hodnot z SQL výsledkové sady a jejich naplnění do instance požadované třídy. Výhodný je tento přístup zejména tehdy, když máme více metod pro načítání téhož typu objektů – např. jednou vracíme kolekci všech záznamů, jindy jen jeden konkrétní nebo podmnožinu – potom máme mapovací kód pěkně na jednom místě a když třeba přidáme do tabulky nový sloupeček, změnu v datové vrstvě děláme jen na jednom místě. Pro každou třídu/tabulku potřebujeme jeden RowMapper.

Jedná se vlastně o takový předstupeň ORM (objektově-relačního mapování), ovšem funguje jen pro načítání dat a ne jejich ukládání.

Pozor na nekontrolované výjimky

Další věc, které si nelze nevšimnout, je absence odchytávání výjimek. Spring totiž převádí kontrolované SQL výjimky na běhové (nekontrolované). Běhové výjimky nemusíme odchytávat (resp. kompilátor nás k tomu nedonutí), a tak chyba vyletí tak vysoko, kam až ji pustíme.

Pokud tedy nejsme dostatečně svědomití a nedoplníme dobrovolně kód pro ošetření chyb, odchytí výjimku až aplikační server a k uživateli se dostane v podobě standardní 500 HTTP chybové stránky. Už ve druhém díle jsme se naučili psát vlastní chybové stránky – pokud si je tedy nezapomeneme nastavit, k uživateli se až tak ošklivá chybová hláška nedostane. Přesto bychom na odchytávání výjimek neměli úplně rezignovat a ušetřené try { … } catch ( … ) { … } se nám přesunou jen do jiné části aplikace (ale mohou být centralizované a nemusí se tolik opakovat).

Velikost aplikace

Jak už to u frameworků bývá, zvyšují datovou velikost naší aplikace. V tomto konkrétním případě vzrostla velikost souboru nekurak.net-web.war (zkompilovaná aplikace) z 32 kilobajtů na úctyhodné 3 megabajty. Spring nabízí opravdu mnohem víc než jen JdbcTemplate a když už si ho do své aplikace zavlečete, bylo by škoda využívat z jeho potenciálu jen tak málo.

Zaujaly vás možnosti Javy a chcete se dozvědět o tomto jazyce víc? Akademie Root nabízí školení Základy programovacího jazyka Java a Pokročilejší kurz jazyka Java, na nichž se naučíte, jak tento multiplatformní objektově orientovaný jazyk používat.

Hibernate ORM

Hibernate je middleware pro objektově-relační mapování a persistenci dat. Použití ORM nám může ušetřit spoustu duplicitního a nudného kódu, ale i přidělat starosti, je to horké téma nejen do internetových diskusí. V článku se této polemice raději vyhnu (můžeme diskutovat pod ním) a podíváme se na praktický příklad – jednoduchou ukázku použití Hibernatu.

Nejprve si doinstalujeme podporu Hibernate do našeho Glassfishe. Pomocí webového rozhraní a nástroje Update Tool:

Java na webovém serveru 4

Glassfish si potřebné knihovny sám stáhne a potom je potřeba aplikační server restartovat.

S Hibernatem nebudeme pracovat přímo, ale pomocí tzv. Java Persistence API (JPA), což je abstraktní vrstva a Hibernate je jen jednou z několika implementací ORM, které v JPA můžeme používat (další jsou třeba TopLink nebo OpenJPA).

Poznámka: pro potřeby persistence jsem trochu přeuspořádal náš projek, nyní se skládá ze čtyř částí:

  • nekurak.net-ear – zastřešující „enterprise“ projekt, který budeme nasazovat na server (obsahuje v sobě níže uvedené projekty)
  • nekurak.net-war – původní webová vrstva: JSP a JavaBeany
  • nekurak.net-ejb – EJB vrstva: zde budeme pracovat s Hibernatem
  • nekurak.net-lib – společné knihovny – DTO a rozhraní

Konfigurace a mapování

Nejdůležitějším konfiguračním souborem je persistence.xml, ve kterém definujeme tzv. persistentní jednotku (PU) a JNDI jméno datového zdroje, který bude používat:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
    xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
  <persistence-unit name="nekurak.net-PU" transaction-type="JTA">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>jdbc/nekurak</jta-data-source>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties>
      <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
      <property name="hibernate.hbm2ddl.auto" value="validate"/>
      <property name="hibernate.max_fetch_depth " value="3"/>
      <property name="hibernate.default_batch_fetch_size" value="16"/>
      <property name="hibernate.order_updates" value="true"/>
      <property name="hibernate.order_inserts" value="true"/>
      <property name="hibernate.show_sql" value="false"/>
    </properties>
  </persistence-unit>
</persistence>

Dále musíme provést vlastní mapování tabulek relační databáze na objekty. K tomu se používají buď anotace uvnitř javových tříd, nebo XML soubory. Mapování pomocí XML vypadá následovně – soubor  Podnik.hbm.xml:

<?xml version="1.0"?>



<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"



"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">



<hibernate-mapping>



    <class name="cz.frantovo.nekurak.dto.Podnik" table="podnik">



    <id name="id" column="id" type="integer"/>



    <property name="nazev" column="nazev"/>



    </class>
</hibernate-mapping>

Datová vrstva realizovaná pomocí JPA

S daty pracujeme pomocí „entitního manažera“ – použití vidíte ve třídě  PodnikHibernateDAO:

@Stateless
public class PodnikHibernateDAO implements PodnikHibernateDAORemote {
    @PersistenceContext(unitName = "nekurak.net-PU")
    private EntityManager em;
    public Collection<Podnik> getPodniky() {
Query dotaz = em.createQuery("FROM " + t(Podnik.class) + " o ORDER BY nazev");
        return dotaz.getResultList();
    }
    private static String t(Class trida) {
        return trida.getSimpleName();
    }
}

K dotazování používáme jiný jazyk než SQL – EJB-QL resp. JPQL. Tento jazyk má daleko blíže k javovým objektům než k relačním tabulkám, proto není až tak užitečné vyčleňovat ho do samostatných souborů, jako jsme to dělali s SQL. Dotazy můžeme psát jako obyčejné textové řetězce, ale můžeme je i poskládat z názvů tříd – viz metoda t()  – díky tomu můžeme na dotazy používat refaktoring. Pokud bychom se např. rozhodli přejmenovat třídu Podnik na Hospoda, stačí ji refaktorovat a nemusíme ručně procházet všechny dotazy. Přehlednější a užitečnější zápis nechť si vybere každý sám.

Stejně jako v případě Springu se jedná o velmi rozsáhlou problematiku a každé z těchto témat by vydalo na samostatný seriál. Proto tento díl berte hlavně jako nástin možností a inspiraci k dalšímu studiu.

Závěr

Volba frameworku je vždy obtížné rozhodnutí a pokud situaci řešíte týmově, vstupují do hry navíc i rozdílné osobní preference jednotlivých kolegů. Neexistuje univerzální řešení a tohle rozhodnutí za vás nikdo neudělá – musíte vycházet ze svých zkušeností, z požadavků konkrétního projektu a znalostí vývojářů. Věřím, že čtyři možnosti nastíněné v tomto a předchozím díle vám s rozhodováním pomůžou.

V komentářích se prosím vyjádřete, jaká další témata by vás zajímala – v plánu jsou např. autorizace/au­tentizace, lokalizace, výstupní formátování, EJB.

Odkazy

Franta Kučera působí jako Java vývojář na volné noze. Programování je jeho koníčkem už od dětství. Kromě toho má rád Linux, relační SŘBD a XML.

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Komentáře: 49

Přehled komentářů

lenin.power dobry serial
bender ORM mapovani
Jakub D. Re: ORM mapovani
František Kučera Re: ORM mapovani
hisaak Re: ORM mapovani
František Kučera Re: ORM mapovani
František Kučera Re: ORM mapovani
pr.rybar Re: ORM mapovani
avatar Re: ORM mapovani
tomáš rychlost
bender Re: rychlost
skorbut Re: rychlost
pr.rybar Re: rychlost
podlesh Re: rychlost
kert Re: rychlost
František Kučera Re: rychlost
Jakub D. Re: rychlost
pr.rybar Re: rychlost
bender Re: rychlost
bender Re: rychlost
František Kučera Re: rychlost
pr.rybar Re: rychlost
alef0 Re: rychlost
pr.rybar Re: rychlost
Dworkin Re: rychlost
Javista Re: rychlost
pr.rybar Re: rychlost
Jakub D. Re: rychlost
Jakub D. Re: rychlost
Tomáš J. Kouba Re: rychlost
alef0 Re: rychlost
pr.rybar Re: rychlost
František Kučera REST vs. RPC
pr.rybar Re: REST vs. RPC
František Kučera Re: REST vs. RPC
pr.rybar Re: REST vs. RPC
Mayo Re: rychlost
pr.rybar Re: rychlost
Jaro Re: rychlost
backup Re: rychlost
pr.rybar Re: rychlost
alef0 Re: rychlost
František Kučera Re: rychlost
alef0 Re: rychlost
Ivan A jak se tohle ladi?
alef0 Re: A jak se tohle ladi?
v6ak Re: A jak se tohle ladi?
smilelover Proc Hibernate?
smilelover Re: Proc Hibernate?
Zdroj: https://www.zdrojak.cz/?p=3168