8. díl - Programování databázového Wrapperu v Javě - Dokončení

Java Databáze Programování databázového Wrapperu v Javě - Dokončení

V minulém dílu seriálu tutoriálů o databázích v Javě jsme rozepsali databázový wrapper v Javě. V tomto posledním díle si doděláme metodu select, díky které si budeme načítat data z databáze. Poté všechny dosavadní metody otestujeme a nakonec si ukážeme, jak bychom mohli náš wrapper rozšířit.

Dokončení Wrapperu - metoda select

Tato metoda bude pracovat jinak než metody save, delete a update. Nebude využívat metodu query, protože v té voláme metodu executeUpdate, která vrací počet ovlivněných řádků. V metodě naopak budeme vracet ResultSet, který vytvoříme za pomoci metody executeQuery.

public class Database {
    //atribut třídy
    //metody tříd
    /**
     * Vrací údaje z databázové tabulky
     * @param table
     * @param columns
     * @param params
     * @return
     * @throws SQLException
     */
    public ResultSet select(String table, Object[] columns, Object[] params) throws SQLException{
        return this.select(table, columns, "", params);
    }

    /**
     * Vrací údaje z databázové tabulky
     * @param table
     * @param columns
     * @param requirement
     * @param params
     * @return
     * @throws SQLException
     */
    public ResultSet select(String table, Object[] columns, String requirement, Object[] params) throws SQLException{
        query = new Query();
        query.select(columns)
             .from(table)
             .where(requirement);

        PreparedStatement ps = connection.prepareStatement(query.getQuery());
        if(params != null){
            int index = 1;
            for(Object param : params){
            ps.setObject(index, param);
            index++;
         }
        }

        ResultSet rs = ps.executeQuery();
        return rs;
    }
}

Metoda select je poslední metoda, kterou v našem wrapperu implementujeme. Najdeme zde jedno přetížení. Můžeme volat metodu select s podmínkou a nebo bez ní.

Testování wrapperu

Tak třídu Database máme hotovou a teď se přesuneme do třídy wrapper, kterou nám vygeneroval NetBeans. Všechny provedené změny můžete pozorovat ve svém grafickém vývojovém databázovém prostředí. Nejprve otestujeme připojení:

package wrapper;


import DB.Database;
import java.sql.SQLException;


public class Wrapper {


    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        try {
            Database database = new Database("osoby", "root", "");
        } catch (SQLException ex) {
            System.out.println("error - "+ex.getMessage());
        }
    }

}

Kód otestujte. Vše by mělo být v pořádku. Neměla by se vám zobrazit žádná chybová hláška. Odteď budeme připisovat kód pouze do metody main. Budu tedy psát pouze obsah metody main. Zbytek třídy nebudu v dalších částech vypisovat.

zápis

try {
        Database database = new Database("osoby", "root", "");
            Object[] firstParams = {22, "Galí", 43, "PHP"};
            int uspech1 = database.insert("programatori", firstParams);
            System.out.println("Uložení uživatele = "+uspech1);
} catch (SQLException ex) {
         System.out.println("error - "+ex.getMessage());
}

Měli byste dostat výsledek:

Uložení uživatele = 1
BUILD SUCCESSFUL (total time: 1 second)*

Postup (Metody insert a values v třídě Query)

V prvním kroku po skončení cyklu for, dostaneme následující podobu SQL dotazu:

INSERT INTO programatori VALUES(?,?,?,?,

Díky metodě deleteCharAt, které posíláme jako parametr index posledního výskytu čárky v poli Stringů, který získáme za pomoci metody sb.lastIndexOf, vymažeme poslední čárku. A nakonec přidáme konec závorky a středník. Poté bude dotaz kompletní a bude vypadat následovně:

INSERT INTO programatori VALUES(?,?,?,?);

Dotaz se následně spustí metodou query.

Vymazání uživatele

Database database = new Database("osoby", "root", "");

Object[] params2 = {"Galí"};
        int uspech2 = database.delete("programatori", "jmeno = ?", params2);
        System.out.println("Smazání uživatele Gali = "+uspech2);

Výstup:

Smazání uživatele Gali = 1
BUILD SUCCESSFUL (total time: 1 second)

Přepsání uživatele

Database database = new Database("osoby", "root", "");

String[] columns = {"jmeno","vek","jazyk"};
            Object[] params = {"Galileo", 18, "Java", 6};
            int uspech4 = database.update("programatori", columns, "id = ?", params);
            System.out.println("Přepsání uživate = "+uspech4);

Výstup :

Přepsání uživate = 1
BUILD SUCCESSFUL (total time: 1 second)

Postup:

Metoda před cyklem for vygereruje následující sql dotaz:

UPDATE osoby SET

Po cyklu for bude vypadat dotaz takto:

UPDATE osoby SET jmeno = ? ,vek = ? ,

A po odstranění poslední čárky a po přidání podmínky bude dotaz v konečné podobě:

UPDATE osoby SET jmeno = ? ,vek = ? where id = ?

Výpis

String[] columns3 = {"jmeno","jazyk"};
            Object[] params3 = {"Java"};
            ResultSet rs = database.select("programatori", columns3, "jazyk = ?", params3);
}

Tato metoda pracuje trochu jinak, než ostatní metody. Její návratová hodnota je ResultSet a k vypsání uživatelů použijeme cyklus. Takto by vypadalo vypsání:

while(rs.next()){
      System.out.println(rs.getString("jmeno")+" - "+rs.getString("jazyk"));
}

Výstup:

James - java
Danna - java
Galileo - Java
Amy - java
BUILD SUCCESSFUL (total time: 1 second)

Vylepšení wrapperu

Takhle vypadající knihovna by nám v aplikacích na mnoho věcí stačila. Pojďme si ale ukázat jak naší třídu ještě vylepšit

Metoda count - počet řádků v databázové tabulce:

public class Database {
    //atribut třídy
    //metody tříd

    /**
     *
     * @param table
     * @return
     * @throws SQLException
     */
    public int count(String table) throws SQLException{
        PreparedStatement ps = connection.prepareStatement("SELECT COUNT(*) FROM "+table);
        ResultSet result = ps.executeQuery();
        result.next();
        return result.getInt(1);
    }

}

Tabulku tam musíme dosadit natvrdo "SELECT COUNT(*) FROM "+table. Jméno tabulky totiž nemůžeme dát jako parametr metodě setString.

Test celé třídy Database

public static void main(String[] args) {
    try {
            Database database = new Database("osoby", "root", "");
            Object[] firstParams = {22, "Galí", 43, "PHP"};
            int uspech1 = database.insert("programatori", firstParams);
            System.out.println("Uložení uživatele = "+uspech1);

            Object[] params2 = {"Galí"};
            int uspech2 = database.delete("programatori", "jmeno = ?", params2);
            System.out.println("Smazání uživatele Gali = "+uspech2);

            String[] columns = {"jmeno","vek","jazyk"};
            Object[] params = {"Galileo", 18, "Java", 6};
            int uspech4 = database.update("programatori", columns, "id = ?", params);
            System.out.println("Přepsání uživate = "+uspech4);

            String[] columns3 = {"jmeno","jazyk"};
            Object[] params3 = {"Java"};
            ResultSet rs = database.select("programatori", columns3, "jazyk = ?", params3);

            while(rs.next()){
                System.out.println(rs.getString("jmeno")+" - "+rs.getString("jazyk"));
            }
        System.out.println(database.count("programatori"));
    } catch (SQLException ex) {
        System.out.println("error - "+ex.getMessage());
    }
}

Tak a to je k naší aplikaci vše. Wrapper můžete otestovat na vašich aplikacích a poté mi určitě napište zpětnou vazbu. Například co by jste do wrapperu doplnili nebo co by jste udělali jinak. Hotovou knihovnu si můžete stáhnout dole pod článkem i se zdrojovými kódy.


 

Stáhnout

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

 

  Aktivity (1)

Článek pro vás napsal Milan Gallas
Avatar
Autor se věnuje programování, hardwaru a počítačovým sítím.

Jak se ti líbí článek?
Celkem (3 hlasů) :
3.666673.666673.666673.66667 3.66667


 



 

 

Komentáře

Avatar
David Čápka
Tým ITnetwork
Avatar
David Čápka:

Hezký seriál, příjemně jsi mě překvapil. Co se týče vylepšení, tak bych určitě pracoval s Connection, Statementy a ResultSety jen v bloku TWR. Na desktopu je to jedno, ale na webu, kde vytvoříš při každém dotazu nové spojení, by došlo rychle k zahlcení poolu. S ResultSetem to bude komplikovanější, musel bys data asi vracet jen v nějakém poli.

Odpovědět  +2 3.5.2014 11:07
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
coells
Redaktor
Avatar
coells:

Navrhovat API je vždycky poměrně složitá záležitost a bývá to náročné i pro zkušené programátory. Na začátečníka tvůj wrapper není až tak špatný. Ale pokud budu realista a představím si, že bych měl tvůj wrapper denně používat, je to hrůza. Jenže jak to opravit?

Když navrhuješ API, vždy se musíš snažit dodržet následující koncepty

  • konzistence
  • bezpečnost použití
  • jednoduchost

Aby to bylo složitější, tyhle koncepty jdou proti sobě.
Jednoduchost většinou narušuje konzistenci a bezpečnost.
Konzistence a bezpečnost použití zase vedou ke složitějšímu API.

Kde tvůj wrapper porušuje jednotlivé principy?

  • konzistence - metody select, update, delete se drží SQL, zatímco save nahrazuje insert - to je matoucí
  • konzistence - update vyžaduje WHERE, select zase celé QUERY, delete nechce nic
  • konzistence - návratové hodnoty jsou jeden velký nepořádek - jaký je rozdíl mezi hodnototu ze save, update, delete? navíc metoda delete() vrací void?
  • bezpečnost - míchat sémantiku různých hodnot do proměnného počtu parametrů... to se nedělá - co když napíšu database.dele­te("programato­ri", "id <> ?", null, 5) - taková bota se stane snadno, ještě častěji to uděláš u updatu a data jsou ... víš kde
  • bezpečnost - metoda delete() je chyba, chyba, chyba - jak často chceš vymazat celou tabulku?
  • jednoduchost - z celého kódu je vidět, že ses snažil mít co nejjednodušší API, jenže paradoxně jsi jednoduchost pokazil, protože čitelnost není zrovna nejlepší

Jednoduché API musí být snadné nejen pro zápis, ale také pro čtení. Co dělá metoda x.save("a", "b"), co dělá metoda x.update("a", "jmeno = ? or id="?", "c", 1, "d", 1)? Kód musí být čitelný i 10 minut poté, co jsi ho napsal.

Co bys měl udělat pro nápravu takových chyb? Vezmi kód z main metody a snaž se ho upravit do co nejlepší podoby, musí být z kódu vidět, co dělá, musí být těžké si překlepem vymazat obsah databáze, musí být snadné takový kód napsat. Někdy to trvá celou řadu iterací, než se dostaneš ke slušnému výsledku. No a pak podle toho uprav celé API.

 
Odpovědět  +5 3.5.2014 13:25
Avatar
ondrejbenes
Člen
Avatar
Odpovídá na David Čápka
ondrejbenes:

Existuje tedy nějaké jednoduché řešení jak API upravit aby nedošlo k zahlcení poolu při běhu aplikace na webu?

 
Odpovědět 15. ledna 12:28
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 3 zpráv z 3.