%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% :db4o %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{Db4o} Db4o (Database for objects) je objektová databáze v~jazyku Java s~otevřeným kódem. Jedná se zcela odlišný koncept než relační databáze. Namísto položek, které by se zapisovaly do sloupců tabulky se zde ukládají celé objekty tak, jak jsou k~dispozici ve zdrojovém kódu jazyka Java. V~této kapitole si databázi db4o přiblížíme víc. Všechny třídy databáze db4o používané v~této kapitole jsou z~balíčku \texttt{com.db4o}, verze databáze je 7.12. \subsection{Knihovna db4o} Knihovnu db4o stáhnete jako soubor \emph{db4o-7.12-java.zip}. Ten po rozbalení expanduje na adresářovou strukturu. Nejdůležitější je podadresář \emph{lib}, který obsahuje JAR soubory knihoven. Pro správný chod příkladů v~této kapitole potřebujete soubory \emph{db4o-7.12-core-java5.jar} a~\emph{bloat-1.0.jar} (vnitřní optimalizace dotazů). Toto stačí pro desktopovou aplikaci používající db4o k~ukládání dat. Ostatní soubory jsou pro práci v~architektuře server--klient, clustering nebo pro starší verze Javy. Podrobnosti se dočtete v~dokumentaci \cite{db4o Documentation}. \fcolorbox{boxout}{boxin}{ \parbox{0.98\linewidth}{ Pozor. Pokud budete db4o přidávat do vývojového prostředí NetBeans (nebo obdobného) přes správce knihoven, nelze mechanicky označit všechny JAR soubory v~adresáři \emph{lib} db4o. Došlo by k~přidání knihoven určených pro starou verzi Javy. I~když je od autorů db4o hezké, že pamatují na zpětnou kompatibilitu, přidání těchto souborů by znehodnotilo běh programu a~ten by pak padal. Ze všech souborů JAR je proto nutné ručně vybrat pouze ty pro verzi Javy 5.0. }} \subsection{Soubor databáze} Databáze db4o ukládá data do jediného souboru. Ten nese stejné jméno, jako databáze. Pracuje se s~ním jako s~obyčejným souborem, tedy může být umístněn kdekoliv a~k~jeho otevření je potřeba kromě názvu uvádět také cestu. V~této kapitole budeme pracovat s~příkladem Animalia. Název databáze byl zvolen jako \emph{animals} a~je umístněn v~kořenovém adresáři aplikace. Všimněte si, že po spuštění příkladu se v~adresáři aplikace skutečně vytvoří soubor \emph{animals}. \subsection{Otevření souboru databáze} Objektovou databázi reprezentuje v~programu instance třídy \texttt{ObjectContainer}. Tu získáme zavoláním statické metody \texttt{openFile} třídy \texttt{Db4oEmbedded}. Jako parametr metody zadáme název databáze. Pokud databáze v~tuto chvíli neexistuje, vytvoří se nová. Příklad ukazuje, jak založit a~zároveň otevřít databázi zvířat \emph{animals}. \begin{Verbatim} ObjectContainer db = Db4oEmbedded.openFile("animals"); \end{Verbatim} \subsection{Ukládání do databáze} Do databáze lze ukládat libovolné objekty v~jazyce Java. Ukládáme je přes instanci třídy \texttt{ObjectContainer} pomocí metody \texttt{store}. Ta bere jako parametr objekt, který chceme uložit a~zapíše jej do bufferu databáze. Po zavolání metody \texttt{commit} se data fyzicky zapíší do souboru databáze. Vše je velmi jednoduché. Následující kód ukazuje, jak do databáze uložit objekty dvou zvířat z~aplikace Animalia --- bobra evropského (\emph{Castor fiber}) a~orangutana bornejského (\emph{Pongo pygmaeus}). \begin{Verbatim} ObjectContainer db = Db4oEmbedded.openFile("animals"); \codeHighlight{Animal beaver = new Animal("castor", "fiber");} \codeHighlight{Animal orangutan = new Animal("pongo", "pygmaeus");} \codeHighlight{db.store(beaver);} \codeHighlight{db.store(orangutan);} \codeHighlight{db.commit();} \end{Verbatim} \subsection{Uzavření databáze} Tak jako relační databáze, i~db4o je dobrým zvykem na konci práce uzavřít. U~našeho příkladu, kde program běží v~jednom vlákně to není nezbytně nutné, v~případě nasazení na serveru by ale otevřená a~opuštěná databáze mohla zdržovat komunikaci ostatních klientů. Příklad rozšíříme o~uzavření databáze. \begin{Verbatim} ObjectContainer db = Db4oEmbedded.openFile("animals"); Animal beaver = new Animal("castor", "fiber"); Animal orangutan = new Animal("pongo", "pygmaeus"); db.store(beaver); db.store(orangutan); db.commit(); \codeHighlight{db.close();} \end{Verbatim} Metoda \texttt{close} sama automaticky zavolá metodu \texttt{commit}. Nemůže se tedy stát, že by databáze na konci práce zůstala v~přechodném stavu. \subsection{Prohledávání databáze} Databáze db4o nabízí dvě možnosti, jak vyhledávat objekty v~ní uložené. V~následujícím textu si obě ukážeme. \subsubsection{Objektový prototyp} Objektovým prototypem myslíme šablonu objektu. Vyhledávání se pak provede podle této šablony. Každý objekt uložený v~databázi je srovnán s~šablonou následujícím způsobem: \begin{enumerate} \item Pokud má objekt stejný typ jako šablona (bere se v~úvahu i~hierarchie tříd), postupuje se k~dalšímu bodu. V~opačném případě objekt z~databáze neodpovídá šabloně. \item Pro všechny atributy šablony: \begin{enumerate} \item Pokud je hodnota atributu nastavena na implicitní hodnotu (\texttt{null} u~objektových typů, \texttt{0} u~primitivních datových typů), pak atribut odpovídá a~pokračuje se dál. \item V~opačném případě je hodnota atributu šablony nastavena na nějakou nenulovou hodnotu. Pak proběhne srovnání atributu šablony s~odpovídajícím atributem objektu v~databázi. Toto srovnání se realizuje pomocí operátoru \texttt{==} u~primitivních datových typů, metodou \texttt{equals} u~objektových typů. Pokud jsou hodnoty atributy stejné, pokračujeme dále. V~opačném případě objekt šabloně neodpovídá. \end{enumerate} \item Objekt odpovídá šabloně a~je zařazen do výsledku. \end{enumerate} Prototypu tedy nastavíme hodnoty atributů, které mají být stejné jako hodnoty požadovaného výsledku. Naopak atributy, které nás nezajímají nastavíme na nulu. Tím z~nich uděláme pomyslného žolíka (wildcard), který při srovnání odpovídá libovolné hodnotě. Objektový prototyp vytvoříme velmi jednoduše jako instanci třídy. Této instanci pomocí nastavovacích metod přiřadíme hodnoty požadovaných atributů. Na objektu databáze pak zavoláme metodu \texttt{queryByExample} a~jako parametr ji předáme zkonstruovaný prototyp. Příklad ukazuje, jak v~databázi vyhledat všechny bobry (rod \emph{castor}) se stupněm ohrožení LC (běžný). \begin{Verbatim} ObjectContainer db = Db4oEmbedded.openFile("animals"); Animal beaver = new Animal("castor", "fiber"); \codeHighlight{beaver.setConservation(Conservation.LC);} Animal orangutan = new Animal("pongo", "pygmaeus"); db.store(beaver); db.store(orangutan); db.commit(); \codeHighlight{Animal prototype = new Animal();} \codeHighlight{prototype.setGenus("castor");} \codeHighlight{prototype.setConservation(Conservation.LC);} \codeHighlight{ObjectSet result = db.queryByExample(prototype);} db.close(); \end{Verbatim} \fcolorbox{boxout}{boxin}{ \parbox{0.98\linewidth}{ Z~výše zmíněného postupu srovnávání s~šablonou jsou patrné dvě věci. Za prvé není možné tímto způsobem hledat objekty podle atributu, jehož hodnota je \texttt{null} nebo \texttt{0}. Nemůžeme tedy vyhledat všechny zvířata, jimž jsme nenastavili rozpětí výšky nebo hmotnosti. Druhou nevýhodou je, že nelze provést srovnání hodnoty atributu podle jiného kritéria než je ekvivalence. }} \subsection{Výsledek prohledávání} Výsledkem prohledávání databáze je kolekce typu \texttt{ObjectSet} ve které jsou uloženy všechny vyhledané objekty. Třída \texttt{ObjectSet} je potomkem třídy \texttt{List} ze standardní knihovny kolekcí v~Javě, s~jejími instancemi lze tedy pracovat jako se standardními kolekcemi, například ji procházet cyklem \emph{foreach} (zaveden ve~verzi 5.0 jazyka Java, bližší vysvětlení je v~publikaci \cite{Novinky Java}). Příklad ukazuje, jak zpracovat všechna vyhledaná zvířata a~vytisknout jejich rodové a~druhové jméno. \begin{Verbatim} ObjectContainer db = Db4oEmbedded.openFile("animals"); Animal beaver = new Animal("castor", "fiber"); beaver.setConservation(Conservation.LC); Animal orangutan = new Animal("pongo", "pygmaeus"); db.store(beaver); db.store(orangutan); db.commit(); Animal prototype = new Animal(); prototype.setGenus("castor"); prototype.setConservation(Conservation.LC); ObjectSet result = db.queryByExample(prototype); \codeHighlight{for (Animal animal : result) \{} \codeHighlight{ System.out.println(animal.getGenus() + " " + animal.getSpecies());} \codeHighlight{\}} db.close(); \end{Verbatim} \subsection{Prohledávání pomocí predikátu} Prohledávání pomocí predikátu nám umožňuje přesně zvolit kritéria srovnávání hodnot atributů. Stačí vytvořit objekt typu \texttt{Predicate} a~implementovat jeho metodu \texttt{match}. Třída \texttt{Predicate} je abstraktní, dobrým řešením pro vytvoření její instance je anonymní třída. Metoda \texttt{match} se chová obdobně jako metoda \texttt{equals} v~jazyce Java. Jako parametr bere objektový typ, v~jejím těle pak provedeme libovolné srovnání a~vrátíme hodnotu \texttt{true} pokud odpovídá, \texttt{false} pokud nikoliv. Jako bonus je třída \texttt{Predicate} parametrizovatelná pomocí typové šablony, tím pádem vrací stejný objektový typ, na jaký se ptáme. Není proto potřeba přetypovávat. Jakmile máme predikát zkonstruovaný, můžeme zavolat na objektu databáze metodu \texttt{query}. Příklad ukazuje, jak vyhledat všechny bobry pomocí predikátu. \begin{Verbatim} ObjectContainer db = Db4oEmbedded.openFile("animals"); Animal beaver = new Animal("castor", "fiber"); beaver.setConservation(Conservation.LC); Animal orangutan = new Animal("pongo", "pygmaeus"); db.store(beaver); db.store(orangutan); db.commit(); \codeHighlight{ObjectSet result = db.query(new Predicate() \{} \codeHighlight{ @Override} \codeHighlight{ public boolean match(Animal animal) \{} \codeHighlight{ return animal.getGenus().equals("castor");} \codeHighlight{ \}} \codeHighlight{\});} \codeHighlight{for (Animal animal : result) \{} \codeHighlight{ System.out.println(animal.getGenus() + " " + animal.getSpecies());} \codeHighlight{\}} db.close(); \end{Verbatim} Podle dokumentací db4o \cite{db4o Documentation} a~\cite{db4o API} jsou predikáty hlavním doporučeným způsobem k~prohledávání databáze. Navíc jsou vnitřně optimalizované pro rychlejší zpracování. \subsection{Upravení objektu v~databázi} Aby bylo možné objektu uloženému v~databázi upravit hodnotu nějakého atributu, je potřeba aby pro tento atribut měl veřejnou nastavovací (\texttt{set}) metodu. Pak už jen stačí objekt z~databáze získat jakoukoliv metodou popsanou výše a~použít příslušnou nastavovací metodu k~zadání nové hodnoty atributu. Nakonec objekt uložíme metodou \texttt{store} volanou na používané instanci třídy \texttt{ObjectContainer}. V~příkladu jsme si uvědomili, že jsme orangutanu bornejskému zapomněli nastavit stupeň ohrožení. Implicitní hodnota je LC (běžný), jeho skutečný stupeň ohrožení je však EN (ohrožený). Ukažme si tedy, jak tuto hodnotu změnit. \begin{Verbatim} ObjectContainer db = Db4oEmbedded.openFile("animals"); ObjectSet result = db.queryByExample(new Animal("pongo", "pygmaeus")); Animal orangutan = result.next(); orangutan.setConservation(Conservation.EN); db.store(orangutan); db.commit(); \end{Verbatim} \fcolorbox{boxout}{boxin}{ \parbox{0.98\linewidth}{ Pokud bychom zapomněli objekt z~databáze nejprve získat a~namísto toho vytvořili nového orangutana, došlo by k~uložení duplicity (i~když s~pozměněnými hodnotami atributů). To je logické, jelikož db4o nepracuje s~primárními klíči a~nedokáže tedy poznat, že jakožto uživatelé zvířata rozpoznáváme podle rodového a~druhového jména. Proto pokud bychom v~příkladu nahradili řádek s~voláním metody \texttt{queryByExample} spolu s~následujícím řádkem konstrukcí \texttt{Animal orangutan = new Animal("pongo", "pygmaeus")} byly v~databázi ve výsledku uložení dva orangutani --- jeden se stupněm ohrožení LC (běžný) a~druhý se stupněm EN (ohrožený). }} \subsection{Smazání objektu z~databáze} Smazání objektu používá stejný mechanismus jako upravení objektu. Nejprve je potřeba objekt z~databáze získat. Poté stačí na používané instanci třídy \texttt{ObjectContainer} zavolat metodu \texttt{delete} a~jako parametr ji předat vyhledaný objekt. Příklad ukazuje, jak z~databáze smazat bobra evropského. \begin{Verbatim} ObjectContainer db = Db4oEmbedded.openFile("animals"); ObjectSet result = db.queryByExample(new Animal("castor", "fiber");); Animal beaver = result.next(); db.delete(beaver); db.commit(); \end{Verbatim} \fcolorbox{boxout}{boxin}{ \parbox{0.98\linewidth}{ Databáze db4o neumožňuje mazat podle prototypů. Takový postup by mohl být velmi nebezpečný a~mohl by vést k~nechtěnému smazání velkého objemu dat z~databáze. }} \subsection{Nasazení na serveru} Doposud jsme pracovali s~databází uloženou v~souboru na stejném počítači, na kterém běžela klientský kód. V~reálném životě ale daleko častěji potřebujeme databázi nasazenou na vzdáleném serveru ke které se klient připojí přes síťové rozhraní. Databáze Db4o řeší i~tuto potřebu. Přitom si zachovává svou jednoduchost a~eleganci. Pro architekturu server--klient je potřeba přidat do adresáře \emph{lib} doplňkový soubor \emph{db4o-7.12-cs-java5.jar} (je součástí knihovny db4o). \subsubsection{Server} Databáze je na serveru uložena ve formě souboru. Síťové rozhraní otevřeme pomocí statické metody \texttt{openServer} třídy \texttt{Db4oClientServer}. Jako parametry metody zadáváme název databázového souboru (obdobně jako u~metody \texttt{openFile}), spolu s~komunikačním portem. Ten můžeme zvolit libovolně, je potřeba, aby byl z~venku přístupný (pomůže nám s~tím správce serveru). Návratovou hodnotou metody \texttt{openServer} je instance typu \texttt{ObjectServer}. Na této instanci je ještě potřeba zavolat metodu \texttt{grantAccess}, která zpřístupní databázi uživateli se zadaným uživatelským jménem a~heslem. Příklad ukazuje jako otevřít databázi \emph{animals.db4o} a~zpřístupnit ji uživateli \emph{netukar} s~heslem \emph{db4oadmin}. Příklad předpokládá, že je spouštěný na serveru \emph{db.animalia.com}. \begin{Verbatim} ObjectServer server = Db4oClientServer.openServer("animals", 8080); server.grantAccess("netukar", "db4oadmin"); \end{Verbatim} \fcolorbox{boxout}{boxin}{ \parbox{0.98\linewidth}{ Databáze db4o používá pro komunikaci protokol TCP/IP a~dokáže se o~všechno postarat sama. Nepracuje jako běžná webová aplikace v~jazyce Java, proto se zde nekonfiguruje žádný servlet. }} \subsubsection{Klient} Vše co klient potřebuje je získat instanci třídy \texttt{ObjectContainer}. Namísto metody \texttt{openFile} použije metodu \texttt{openClient} třídy \texttt{Db4oClientServer}. Jako parametry je potřeba zadat adresu serveru, port, uživatelské jméno a~heslo. Klient pak s~databází pracuje úplně stejně jako v~případě lokální databáze. V~příkladu se připojujeme k~databázi \emph{db.animalia.com} přes port \texttt{8080} a~zadáváme uživatelské jméno \emph{netukar} spolu s~heslem \emph{db4oadmin}. \begin{Verbatim} String host = "db.animalia.com"; int port = 8080; String user = "netukar"; String password = "db4oadmin"; ObjectContainer db = Db4oClientServer.openClient(host, port, user, password); \end{Verbatim} Všimněte si, že pro klienta připojeného přes síť je název databázového souboru irelevantní. \subsection{Db4o a~relační databáze} Db4o je projekt s~otevřeným kódem a~nemá proto takovou technologickou podporu jako například databáze Oracle. Přestože by se mohlo zdát, že ukládání dat jakožto objektů a~následné strukturované porovnávání musí být zákonitě pomalejší, než jakákoliv relační databáze, není tomu tak. Db4o je uznávaná databáze a~podle srovnání \cite{db4o Performance} je při 30000 objektech v~databázi stále rychlejší než třeba populární relačná databáze MySQL. \subsection{Závěr} Db4o představuje výbornou implementaci objektové databáze v~jazyce Java. Mezi její přednosti patří přehlednost, elegance, intuitivní názvy tříd a~metod a~v~neposlední řadě také výborná dokumentace tříd a~metod. S~databází se pracuje velmi jednoduše a~přitom efektivně. Další výhodou je možnost její vestavění do desktopových aplikací pro pohodlný způsob ukládání dat (namísto XML). Mezi nevýhody lze připočíst naprosté minimum materiálů, jak publikací, tak online článků. Naučit se s~databází dobře pracovat a~využít všechny její možnosti tedy zabere větší množství času a~experimentování. Použitá literatura:\\ \cite{db4o Wikipedia} \emph{Db4o na Wikipedii}\\ \cite{db4o Documentation} \emph{Dokumentace db4o 7.12}\\ \cite{db4o Tutorial} \emph{Online tutoriál}\\ \cite{db4o API} \emph{Dokumentace tříd db4o 7.12}\\ \cite{db4o Guide} \emph{Návod prohledávání databáze}\\ \cite{db4o Performance} \emph{Srovnání výkonu vybraných databází}\\