Internet Info, s.r.o. Lupa Měšec Podnikatel Root Zdroják DigiZone Slunečnice Vitalia TopDrive KupDnes Navrcholu NovýTarif Dobrý web Weblogy Woko Jagg Computer.cz SK: MojeLinky

Hlavní navigace

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.

Tweetni to Twitter Jaggni to! Jagg Del.icio.us Delicious

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

František Kučera

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.

Školení Google+ pro firmy

DW - Školení PPC
  • Jak využít Google+ pro firemní komunikaci a marketing.
  • Čím se liší Google+ od Twitteru a Facebooku z pohledu firemního využití.
  • Jak využít Google+ v souladu s pravidly užívání.
  • Založení Google+ Page (Stránky) krok po kroku, včetně praktických tipů.

Detailní informace o školení Google+ »

Přehled názorů

dobry serial
Lenin POWER! 12. 2. 2010 01:49
Nový
ORM mapovani
bender 12. 2. 2010 10:33
Nový
├ 
Re: ORM mapovani
Jakub D. 12. 2. 2010 13:20
Nový
└ 
Re: ORM mapovani
Franta Kučera 12. 2. 2010 13:57
Nový
 
├ 
Re: ORM mapovani
hisaak 13. 2. 2010 17:21
Nový
 
│
└ 
Re: ORM mapovani
Franta Kučera 13. 2. 2010 21:38
Nový
 
│
 
└ 
Re: ORM mapovani
Franta Kučera 13. 2. 2010 21:47
Nový
 
│
 
 
└ 
Re: ORM mapovani
Peter Rybar 13. 2. 2010 22:10
Nový
 
└ 
Re: ORM mapovani
avatar 22. 2. 2010 21:26
Nový
rychlost
tomáš 12. 2. 2010 11:45
Nový
├ 
Re: rychlost
bender 12. 2. 2010 12:54
Nový
├ 
Re: rychlost
skorbut 12. 2. 2010 13:05
Nový
│
└ 
Re: rychlost
Peter Rybar 12. 2. 2010 14:54
Nový
│
 
├ 
Re: rychlost
podlesh 12. 2. 2010 15:24
Nový
│
 
│
└ 
Re: rychlost
kert 12. 2. 2010 15:40
Nový
│
 
└ 
Re: rychlost
Franta Kučera 12. 2. 2010 15:28
Nový
│
 
 
└ 
Re: rychlost
Jakub D. 12. 2. 2010 17:26
Nový
│
 
 
 
└ 
Re: rychlost
Peter Rybar 12. 2. 2010 18:11
Nový
│
 
 
 
 
├ 
Re: rychlost
bender 13. 2. 2010 08:49
Nový
│
 
 
 
 
│
└ 
Re: rychlost
bender 13. 2. 2010 08:51
Nový
│
 
 
 
 
└ 
Re: rychlost
Franta Kučera 14. 2. 2010 13:53
Nový
│
 
 
 
 
 
├ 
Re: rychlost
Peter Rybar 14. 2. 2010 16:33
Nový
│
 
 
 
 
 
│
├ 
Re: rychlost
alef0 16. 2. 2010 09:20
Nový
│
 
 
 
 
 
│
│
└ 
Re: rychlost
Peter Rybar 9. 3. 2010 11:38
Nový
│
 
 
 
 
 
│
└ 
Re: rychlost
Dworkin 21. 2. 2010 11:45
Nový
│
 
 
 
 
 
│
 
└ 
Re: rychlost
Javista 21. 2. 2010 11:59
Nový
│
 
 
 
 
 
│
 
 
└ 
Re: rychlost
Peter Rybar 21. 2. 2010 17:13
Nový
│
 
 
 
 
 
└ 
Re: rychlost
Jakub D. 14. 2. 2010 18:09
Nový
├ 
Re: rychlost
Jakub D. 12. 2. 2010 13:24
Nový
│
└ 
Re: rychlost
Tomáš J. Kouba 14. 2. 2010 21:44
Nový
│
 
├ 
Re: rychlost
alef0 14. 2. 2010 21:59
Nový
│
 
└ 
Re: rychlost
Peter Rybar 14. 2. 2010 22:10
Nový
│
 
 
├ 
REST vs. RPC
Franta Kučera 14. 2. 2010 23:10
Nový
│
 
 
│
└ 
Re: REST vs. RPC
Peter Rybar 15. 2. 2010 11:12
Nový
│
 
 
│
 
└ 
Re: REST vs. RPC
Franta Kučera 18. 2. 2010 15:32
Nový
│
 
 
│
 
 
└ 
Re: REST vs. RPC
Peter Rybar 18. 2. 2010 20:30
Nový
│
 
 
└ 
Re: rychlost
Mayo 15. 2. 2010 16:42
Nový
│
 
 
 
├ 
Re: rychlost
Peter Rybar 15. 2. 2010 17:32
Nový
│
 
 
 
└ 
Re: rychlost
Jaro 18. 2. 2010 00:07
Nový
├ 
Re: rychlost
backup 14. 2. 2010 11:32
Nový
│
└ 
Re: rychlost
Peter Rybar 14. 2. 2010 15:31
Nový
└ 
Re: rychlost
alef0 14. 2. 2010 11:34
Nový
 
└ 
Re: rychlost
Franta Kučera 14. 2. 2010 13:45
Nový
 
 
└ 
Re: rychlost
alef0 14. 2. 2010 22:00
Nový
A jak se tohle ladi?
Ivan 15. 2. 2010 13:30
Nový
├ 
Re: A jak se tohle ladi?
alef0 16. 2. 2010 09:19
Nový
└ 
Re: A jak se tohle ladi?
Vít Šesták 19. 2. 2010 19:56
Nový
Proc Hibernate?
Daniel Kvasnička ml. 24. 3. 2010 08:24
Nový
└ 
Re: Proc Hibernate?
Daniel Kvasnička ml. 24. 3. 2010 08:25
Nový
       

Tento text je již více než dva měsíce starý. Chcete-li na něj reagovat v diskusi, pravděpodobně vám již nikdo neodpoví. Pro řešení aktuálních problémů doporučujeme využít naše diskusní fórum.

Zasílat nově přidané příspěvky e-mailem