# Record collection (JavaScript)

Creació d'una aplicació amb una base de dades per ordenar una col·lecció de música
Projecte de documentació del GNOME
gnome-doc-list@gnome.org
Johannes Schmid
jhs@gnome.org
Marta Maria Casetti
mmcasettii@gmail.com 2013

# Record collection

      En aquest programa d'aprenentatge aprendreu a:

        Connectar-se a una base de dades amb la libgda
        Inserir i navegar registres a una taula d'una base de dades

# Introducció

Aquesta demostració utilitza el llenguatge JavaScript. Es mostrarà com connectar-se i utilitzar una base de dades des d'un programa escrit amb la GTK+ a través de la biblioteca GDA (Accés a dades del GNOME). Haureu d'instal·lar-la per poder seguir aquesta demostració.

El propòsit de la biblioteca GDA (Accés a dades del GNOME) és proporcionar un accés universal a diferents estils i tipus de fonts de dades. Això compren des dels sistemes tradicionals de bases de dades relacions fins a qualsevol tipus de font de dades imaginable com ara un servidor de correu, un directori d'LDAP, etc. Per més informació i la documentació completa de l'API, aneu al lloc web de la GDA.

Tot i que una gran part del codi està dedicat a la interfície d'usuari (GUI), el programa d'aprenentatge està centrat en les parts relacionades amb la base de dades (es farà menció d'altres parts si es consideren rellevants). Per veure més programes escrits en JavaScript pel GNOME, vegeu el programa d'aprenentatge del programa de visualització d'imatges.

# Creació d'un projecte a l'Anjuta

Abans de començar a programar, heu de crear un projecte nou a l'Anjuta. L'Anjuta crearà tots els fitxers necessaris per, més endavant, construir i executar el codi. És molt útil per així mantenir-ho tot junt.

Inicieu l'Anjuta i feu clic a FitxerNouProjecte per obrir l'auxiliar de projectes.

Trieu JavaScript genèric a la pestanya JS, feu clic a Continua i empleneu les dades de les pàgines següents de l'auxiliar. Utilitzeu record-collection com a nom de projecte i de directori.

Feu clic a Aplica i es crearà el projecte. Obriu el fitxer src/main.js des de la pestanya de Projecte o de Fitxer. El fitxer ja conté un exemple de codi molt bàsic.

# Estructura del programa

Aquesta demostració és una aplicació de GTK+ senzilla (amb una sola finestra) que permet inserir registres a una taula d'una base de dades, així com navegar pels registres d'aquesta. La taula conté dos camps: `id`, per desar nombres enters i `name` per desar text de llargada variable. La primera secció (al principi) de l'aplicació permet inserir registres a la taula. La secció de baix (al final) permet veure tots els registres de la taula. S'actualitza el contingut de la taula cada vegada que s'afegeix un registre nou i també quan s'inicia l'aplicació.

# Ara comença lo bo

Primera ullada a l'esquelet del programa:

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 ();

Línies 1 a 4: importacions inicials. Mireu-vos bé la línia 3 que li diu al JavaScript que importi la biblioteca GDA, la biblioteca central d'aquest programa d'aprenentatge.

Línies 6 a 17: defineixen la classe `Demo`. Mireu-vos bé les línies 13 a 15, on es criden als 3 mètodes que faran tota la feina i que es descriuran tot seguit.

Línies 19 a 23: inicien l'aplicació.

# Disseny de l'aplicació

Doneu una ullada al mètode `setupWindow`. És el responsable de crear la interfície d'usuari (UI). Com que la interfície no és l'eix principal del programa d'aprenentatge només es comenta per sobre.

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; });

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

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

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

// ID field 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);

// Name field 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);

// Insert button 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 ();

// Browse textview 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 (); },

Línies 22 i 27: creen dues entrades (per als dos camps) on els usuaris de l'aplicació escriuran alguna cosa per inserir-la a la base de dades.

Línies 31 a 34: creen el botó d'inserció. Es connecta el seu senyal `clicked` al mètode de classe privat `_insertClicked`. Aquest mètode es descriu més endavant.

Línia 30: crea el giny (`TextView`) on es mostrarà el contingut de la taula.

Línia 44: crea l'etiqueta on es mostrarà el nombre de registres de la taula. Inicialment serà buit, s'actualitzarà més endavant.

# Connexió i inicialització de la base de dades

El codi per connectar a la base de dades és en el mètode `setupDatabase` que segueix:

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))"); } },

Línies 2 i 3: creen l'objecte `Connection` de la GDA. S'han de proporcionar unes quantes propietats en el seu constructor:

`provider`: un dels proveïdors amb els que la GDA permet treballar. Són: SQLite, MySQL, PostgreSQL, Oracle i molts d'altres. Per aquesta demostració utilitzarem la base de dades SQLite ja que ve per defecte en la majoria de distribucions i és senzilla de treballar-hi (només fa servir un fitxer com a base de dades).

`cnc_string`: la cadena de connexió. Pot canviar segons el proveïdor. La sintaxi per a l'SQLite és: `DB_DIR=CAMÍ;DB_NAME=NOM_DE_FITXER`. En aquesta demostració farem servir una base de dades anomenada gnome_demo a la carpeta de l'usuari (adoneu-vos de la crida a la funció `get_home_dir` de la GLib).

Si la GDA no sap treballar amb el proveïdor o si a la cadena de connexió li manca algun element, la línia 2 generarà una excepció, de manera que en codi real hauríem de gestionar-ho amb la sentència de JavaScript `try`...`catch`.

Línia 4: obre la connexió. Si no existeix la base dades el proveïdor de SQLite la crearà en aquest moment.

Línies 6 a 10: es realitza un «SELECT» simple per comprovar si existeix la taula (línia 7). Si no existeix (perquè s'acaba de crear la base de dades), aquesta línia generarà una excepció que es gestionarà en el bloc de codi `try`...`catch`. Si és el cas, s'executa la sentència per crear la taula (línia 9).

In order to run the SQL commands above we are using the GDA connection methods `execute_select_command` and `execute_non_select_command`. They are simple to use, and just require two arguments: The `Connection` object and the SQL command to be parsed.

En aquest punt d'execució la base de dades ja està configurada i a punt per utilitzar-se.

# Selecció

Després de connectar-se a la base de dades, el constructor de la demostració crida el mètode `selectData`. Aquest mètode és el responsable d'obtenir els registres de la taula i mostrar-los en el giny `TextView`. Ara es comentarà amb més detall:

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>"; },

Line 2: The `SELECT` command. We are using the GDA connection's `execute_select_command` method for that. It returns a `DataModel` object, which is later used to retrieve the rows.

Línia 3: crea l'objecte `Iter` que s'utilitza per iterar sobre els registres de `DataModel`.

Línia 7: itera sobre tots els registres, els recupera amb l'ajuda de l'objecte `Iter`. En aquest punt la variable `iter` conté les dades actuals recuperades de la base de dades. El seu mètode `move_next` retorna `false` quan arriba a l'últim registre.

Línies 8 i 9: es fan dues coses a cada línia:

S'utilitza el mètode `get_value_at` d'`Iter` que només necessita un paràmetre: el número de la columna a recuperar, començant a partir de zero. Com que el `SELECT` que s'ha fet només retorna dues columnes, els índexs de les columnes són el 0 i l'1.

El mètode `get_value_at` retorna el camp en el format `GValue` de la GLib. Una manera senzilla de convertir aquest format a una cadena és utilitzant la funció global de la GDA `value_stringify`. Això és el que es fa aquí i després es desa el resultat a les variables `id_field` i `name_field`.

Línia 11: es concatenen els dos camps per fer una sola línia de text separada per `"=>"` i desada a la variable `text`.

Línia 14: quan s'acaba la iteració, hi ha tots els registres formatats a la variable `text`. En aquesta línia només s'estableix que el contingut de `TextView` sigui el d'aquesta variable.

Línia 15: mostra el nombre de registres de la taula amb el mètode `get_n_rows` de `DataModel`.

# Inserció

Fins ara s'ha connectat a la base de dades i s'ha mostrat com seleccionar les files de la taula. Ara només falta fer un `INSERT` a la taula. Recordeu que abans en el mètode `setupWindow` s'ha connectat el senyal `clicked` del botó Insert amb el mètode `_insertClicked`? Ara se'n mostra la implementació.

_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 (); },

We have learned how to use the GDA connection's methods `execute_select_command` and `execute_non_select_command` to quickly execute SQL commands on the database. GDA allows one to build a SQL statement indirectly, by using its `SqlBuilder` object. What are the benefits of this? GDA will generate the SQL statement dynamically, and it will be valid for the connection provider used (it will use the same SQL dialect the provider uses). Let's study the code:

Línies 2 i 3: comproven que l'usuari hagi emplenat tots els camps. El codi del mètode privat `_validateFields` és molt senzill i li podeu fer una ullada en el codi font sencer de la demostració.

Línia 5: la forma ràpida de fer un `INSERT`. Està comentat perquè es vol mostrar com utilitzar l'objecte `SqlBuilder` per construir una ordre SQL que sigui vàlida per qualsevol base de dades.

Línia 7: crea l'objecte `SqlBuilder`. S'ha d'informar del tipus d'ordre que es vol construir. Pot ser un `SELECT`, un `UPDATE`, un `INSERT` o bé un `DELETE`.

Línia 8: estableix el nom de la taula on s'efectuarà l'ordre (generarà un `INSERT INTO demo`)

Línies 9 i 10: estableix els camps i els valors que formaran part de l'ordre. El primer argument és el nom del camp (tal i com està definit a la taula). El segon argument és el valor que li volem donar.

Línia 11: s'obté l'objecte generat dinàmicament `Statement` que representa una ordre SQL.

Línia 12: ara ja sí, s'executa l'ordre SQL (un `INSERT`).

Línia 14: es netegen els camps id i nom de la pantalla. El codi del mètode privat `_clearFields` és molt senzill i es pot trobar en el codi font sencer de la demostració.

Línia 15: actualitza la vista de la pantalla fent un altre `SELECT`.

També es poden utilitzar paràmetres mentre construïu l'ordre. Pel fet d'utilitzar objectes `SqlBuilder` i els seus paràmetres es poden evitar atacs d'injecció d'SQL. Per més informació sobre els paràmetres mireu-vos la documentació de la GDA.

# Execució de l'aplicació

Ja s'ha vist tot el codi que fa falta, així que ja es pot executar el codi. S'acaba de crear una aplicació per la gestió d'una col·lecció de música!

# Implementació de referència

Si teniu algun problema amb el programa d'aprenentatge, compareu el codi amb el codi de referència.