# Sbírka alb (JavaScript)

Jak vytvořit malou databázovou aplikaci pro roztřídění vaší hudební sbírky.
Dokumentační projekt GNOME
gnome-doc-list@gnome.org
Johannes Schmid
jhs@gnome.org
Marta Maria Casetti
mmcasettii@gmail.com 2013

# Sbírka alb

      V této lekci se seznámíme s následujícími věcmi:

        Jak se připojit k databázi pomocí libgda.
        Jak vložit záznam do databázové tabulky a jak záznamy procházet.

# Úvod

Tato ukázka používá programovací jazyk JavaScript. Budeme předvádět připojení k databázi a její použití z programu GTK pomocí knihovny GDA (GNOME Data Access). Z toho důvodu potřebujete mít tuto knihovnu nainstalovanou.

GNOME Data Access (GDA) je knihovna, jejímž učelem je poskytovat univerzální přístup do různých druhů a typů datových zdrojů. Ty sahají od tradičních relačních databázových systémů až po jakékoliv představitelné datové zdroje, jako je poštovní server nebo adresář LDAP. Více informací a ucelenou dokumentaci k API najdete na webových stránkách GDA.

Přestože velká část kódu souvisí s uživatelským rozhraním, hodláme zaměřit tuto lekci na databázovou část (i když jiné části můžeme též zmínit, pokud uvidíme nějakou souvislost). Jestli se chcete dozvědět více o programování v jazyce JavaScript v GNOME, podívejte se na lekci Prohlížeč obrázků.

# Vytvoření projektu ve studiu Anjuta

Než začnete s kódováním, musíte ve studiu Anjuta vytvořit nový projekt. Tím se vytvoří všechny soubory, které budete později potřebovat k sestavení a spuštění kódu. Je to také užitečné kvůli udržení všeho pohromadě.

Spusťte IDE Anjuta a klikněte na Soubor Nový Projekt, aby se otevřel průvodce projektem.

Na kartě JS zvolte Obecný JavaScript, klikněte na Pokračovat a na několika následujících stránkách vyplňte své údaje. Jako název projektu a složky použijte record-collection.

Klikněte na Dokončit a vytvoří se vám projekt. Otevřete src/main.js na kartě Projekt nebo Soubor. Měl by obsahovat úplně základní příklad kódu.

# Struktura programu

Tato ukázka je jednoduchou aplikací GTK (s jedním oknem) se schopností vkládat záznamy do databázové tabulky a procházet je. Tabulka má dvě pole: `id` (celé číslo) a `name` (varchar). V první části aplikace (nahoře) můžete vkládat záznamy do tabulky. V poslední části (dole) můžete vidět všechny záznamy v tabulce. Zobrazení obsahu se aktualizuje pokaždé, když je vložen nový záznam a při spuštění aplikace.

# Začíná zábava

Začněme prozkoumáním kostry programu:

const GLib = imports.gi.GLib; const Gtk = imports.gi.Gtk; const Gda = imports.gi.Gda; const Lang = imports.lang;

function Demo () { this._init (); }

Demo.prototype = {

_init: function () { this.setupWindow (); this.setupDatabase (); this.selectData (); } }

Gtk.init (null, null);

var demo = new Demo ();

Gtk.main ();

Řádek 1 – 4: Inicializační importy. Zvláštní pozornost věnujte řádku 3, který říká, aby JavaScript naimportovat knihovnu GDA, na kterou se tato lekce zaměřuje.

Řádek 6 – 17: Definuje naši třídu `Demo`. Zvláštní pozornost věnujte řádkům 13 – 15, kde se volají 3 metody, které udělají celou práci. Podrobněji to bude vysvětleno dále.

Řádek 19 – 23: Spuštění aplikace.

# Návrh aplikace

Podívejme se na metodu `setupWindow`. Ta zodpovídá za vytvoření uživatelského rozhraní. Protože uživatelským rozhraním se teď nezabýváme, vysvětlíme si jen podstatné části.

setupWindow: function () { this.window = new Gtk.Window ({title: "Data Access Demo", height_request: 350}); this.window.connect ("delete-event", function () { Gtk.main_quit(); return true; });

// Hlavní box var main_box = new Gtk.Box ({orientation: Gtk.Orientation.VERTICAL, spacing: 5}); this.window.add (main_box);

// První popisek var info1 = new Gtk.Label ({label: "<b>Insert a record</b>", xalign: 0, use_markup: true}); main_box.pack_start (info1, false, false, 5);

// Vodorovný box pro "Insert a record" var insert_box = new Gtk.Box ({orientation: Gtk.Orientation.HORIZONTAL, spacing: 5}); main_box.pack_start (insert_box, false, false, 5);

// Pole ID insert_box.pack_start (new Gtk.Label ({label: "ID:"}), false, false, 5); this.id_entry = new Gtk.Entry (); insert_box.pack_start (this.id_entry, false, false, 5);

// Pole Name insert_box.pack_start (new Gtk.Label ({label: "Name:"}), false, false, 5); this.name_entry = new Gtk.Entry ({activates_default: true}); insert_box.pack_start (this.name_entry, true, true, 5);

// Tlačítko Insert var insert_button = new Gtk.Button ({label: "Insert", can_default: true}); insert_button.connect ("clicked", Lang.bind (this, this._insertClicked)); insert_box.pack_start (insert_button, false, false, 5); insert_button.grab_default ();

// TextView pro prohlížení var info2 = new Gtk.Label ({label: "<b>Browse the table</b>", xalign: 0, use_markup: true}); main_box.pack_start (info2, false, false, 5); this.text = new Gtk.TextView ({editable: false}); var sw = new Gtk.ScrolledWindow ({shadow_type:Gtk.ShadowType.IN}); sw.add (this.text); main_box.pack_start (sw, true, true, 5);

this.count_label = new Gtk.Label ({label: "", xalign: 0, use_markup: true}); main_box.pack_start (this.count_label, false, false, 0);

this.window.show_all (); },

Řádky 22 a 27: Vytvoří 2 vstupní pole (pro dvě databázová pole), do kterých uživatel bude psát to, co chce do databáze vložit.

Řádek 31 – 34: Vytvoří tlačítko „Insert“. Jeho signál `"clicked"` napojíme na soukromou metodu `_insertClicked` třídy. Tato metoda je podrobněji rozebrána níže.

Řádek 39: Vytvoří widget (`TextView`), ve kterém budeme zobrazovat obsah tabulky.

Řádek 44: Vytvoří popisek, ve kterém budeme zobrazovat počet záznamů v tabulce. Na začátku je prázdný, později bude aktualizován.

# Připojení k databázi a její inicializace

Kód, který provádí připojení do databáze je v metodě `setupDatabase` níže:

setupDatabase: function () { this.connection = new Gda.Connection ({provider: Gda.Config.get_provider("SQLite"), cnc_string:"DB_DIR=" + GLib.get_home_dir () + ";DB_NAME=gnome_demo"}); this.connection.open ();

try { var dm = this.connection.execute_select_command ("select * from demo"); } catch (e) { this.connection.execute_non_select_command ("create table demo (id integer, name varchar(100))"); } },

Rádky 2 – 3: Vytvoří objekt `Connection` z knihovny GDA. Jeho konstruktoru musíme poskytnout některé vlastnosti:

`provider`: Jeden z podporovaných poskytovatelů v knihovně GDA. Podporovány jsou SQLite, MySQL, PostgreSQL, Oracle a řada dalších. Pro předváděcí účely použijeme databázi SQLite, protože je standardně předinstalovaná ve většině distribucí a je jednoduchá na používání (prostě používá soubor jako databázi).

`cnc_string`: Připojovací řetězec. Může si lišit poskytovatel od poskytovatele. Pro SQLite je syntax: `DB_DIR=CESTA;DB_NAME=NÁZEV_SOUBORU`. V této ukázce přistupujeme k databázi s názvem gnome_demo v domovské složce uživatele (všimněte si volání funkce `get_home_dir`).

V případě, že poskytovatel není v GDA podporován nebo že v připojovacím řetězci schází nějaká část, vyvolá řádek 2 výjimku. V reálném použití bychom ji měli obsloužit konstrukcí `try` … `catch` jazyka JavaScript.

Řádek 4: Otevře připojení. U poskytovatele SQLite, když databáze neexistuje, dojde v tomto kroku k jejímu vytvoření.

Řádky 6 – 10: Zkusí udělat jednoduchý dotaz, aby se ověřilo, že tabulka existuje (řádek 7). Pokud neexistuje (protože databáze byla teprve vytvořena), vyvolá tento příkaz výjimku, která se obslouží v bloku `try` … `catch`. V tomto případě provedeme příkaz, který tabulku vytvoří (řádek 9).

Ke spuštění příkazů SQL výše používáme metody připojení GDA s názvem `execute_select_command` a `execute_non_select_command`. Ty jsou jednoduché na použití a požadují jen dva argumenty: objekt `Connection` a příkaz SQL, který se má zpracovat.

V tuto chvíli máme vytvořenu databázi a jsme připraveni ji použít.

# Vybírání

Po připojení do databáze konstruktor v naší ukázce zavolá metodu `selectData`. Ta zodpovídá za získání všech záznamů z tabulky a jejich zobrazení ve widgetu `TextView`. Pojďme se na to podívat:

selectData: function () { var dm = this.connection.execute_select_command ("select * from demo order by 1, 2"); var iter = dm.create_iter ();

var text = "";

while (iter.move_next ()) { var id_field = Gda.value_stringify (iter.get_value_at (0)); var name_field = Gda.value_stringify (iter.get_value_at (1));

text += id_field + "\t=>\t" + name_field + '\n'; }

this.text.buffer.text = text; this.count_label.label = "<i>" + dm.get_n_rows () + " record(s)</i>"; },

Řádek 2: Příkaz `SELECT`. K tomu používáme metodu připojení GDA s názvem `execute_select_command`. Vrací objekt `DataModel`, který je později použit k získání řádků.

Řádek 3: Vytvoří objekt `Iter`, který se používá k procházení záznamů v `DataModel`.

Řádek 7: Ve smyčce prochází všechny záznamy, které získává pomocí objektu `Iter`. V totmo místě proměnná `iter` obsahuje aktuálně získaná data. Jeho metoda `move_next` vrací `false`, když je dosažen poslední záznam.

Řádek 8 – 9: Na každém řádku děláme dvě věci:

Použijeme metodu `get_value_at` třídy `Iter`, která požaduje jen jeden argument: číslo sloupce, který se má získat, počítáno od 0. Náš příkaz `SELECT` vrací dva sloupce, takže získáváme sloupce 0 a 1.

Metoda `get_value_at` vrací pole ve formátu `GValue` knihovny GLib. Jednoduchým způsobem, jak převést tento formát na řetězec, je použít globální funkci knihovny GLib `value_stringify`. Což je přesně to, co zde děláme a výsledky pak uložíme do proměnných `id_field` a `name_field`.

Řádek 11: Spojí dvě pole, aby vytvořila jeden textový řádek, přičemž jsou oddělená pomocí `"=>"`, a uloží je do proměnné `text`.

Řádek 14: Když je smyčka dokončená, máme všechny záznamy naformátované v proměnné `text`. V tomto řádku pomocí ní nastavíme obsah widgetu `TextView`.

Řádek 15: Zobrazí počet záznamů v tabulce pomocí metody `get_n_rows` objektu `DataModel`.

# Vkládání

Nuže, nyní víme, jak se připojit k databázi a jak vybrat řádky z tabulky. Nyní je čas na vkládání do tabulky pomocí `INSERT`. Pamatujete si z dřívějška, jak jsem v metodě `setupWindow` napojili signál `"clicked"` od tlačítka Insert na metodu `_insertClicked`? Pojďme se podívat na implementaci této metody.

_insertClicked: function () { if (!this._validateFields ()) return;

// Gda.execute_non_select_command (this.connection, // "insert into demo values ('" + this.id_entry.text + "', '" + this.name_entry.text + "')");

var b = new Gda.SqlBuilder ({stmt_type:Gda.SqlStatementType.INSERT}); b.set_table ("demo"); b.add_field_value_as_gvalue ("id", this.id_entry.text); b.add_field_value_as_gvalue ("name", this.name_entry.text); var stmt = b.get_statement (); this.connection.statement_execute_non_select (stmt, null);

this._clearFields (); this.selectData (); },

Naučili jsme se, jako používat metody připojení GDA s názvy `execute_select_command` a `execute_non_select_command`, které slouží k rychlému provedení příkazů SQL v databázi. GDA umožňuje vytvořit výraz SQL nepřímo, pomocí jejího objektu `SqlBuilder`. Jaké to přináší výhody? GDA vygeneruje výraz SQL dynamicky a ten bude platný pro použitého poskytovatele připojení (použije se varianta SQL, kterou používá onen poskytovatel). Pojďme se podívat na kód:

Řádky 2 – 3: Zkontroluje se, jestli uživatel vyplnil všechna pole. Kód soukromé metody `_validateFields` je opravdu jednoduchý a můžete si jej přečíst v kompletním kódu této ukázky.

Řádek 5: Rychlejší způsob, jak provést `INSERT`. Zde je zakomentováno, protože chceme ukázat, jak použít objekt `SqlBuilder` k sestavení výrazu SQL přenositelného mezi databázemi.

Řádek 7: Vytvoří objekt `SqlBuilder`. Musíme předat typ výrazu, který se chystáme sestavit. Může to být `SELECT`, `UPDATE`, `INSERT` nebo `DELETE`.

Řádek 8: Nastaví název tabulky, nad kterou bude sestavený výraz operovat (vygeneruje `INSERT INTO demo`)

Řádek 9 – 10: Nastaví pole a jejich hodnoty, které budou součástí výrazu. První argument je název pole (tak, jak je v tabulce). Druhý je hodnota pro toto pole.

Řádek 11: Získá dynamicky vygenerovaný objekt `Statement`, který představuje výraz SQL.

Řádek 12: Nakonec se provede výraz SQL (`INSERT`).

Řádek 14: Smaže pole id a name na obrazovce. Kód pro soukromou metodu `_clearFields` je opravdu jednoduchý a můžete si jej přečíst v kompletním zdrojovém kódu ukázky.

Řádek 15: Aktualizuje zobrazení na obrazovce provedením jiného výrazu `SELECT`.

Můžete také použít parametrické sestavení výrazu. Pomocí objektů `SqlBuilder` a parametrů budu výrazy méně náchylné k útokům, jako je SQL injection. Na další informace o parametrech se podívejte do dokumentace k GDA.

# Spuštění aplikace

Všechen kód, který potřebujete, je již na svém místě, takže jej pojďme zkusit spustit. Nyní máte databázi pro svoji sbírku alb.

# Ukázková implementace

Pokud v této lekci narazíte na nějaké problémy, porovnejte si svůj kód s tímto ukázkovým kódem.