Cree un pequeno aplicativo con unha base de datos para ordenar a súa colección de música
Proxecto de documentación de GNOME
gnome-doc-list@gnome.org
Johannes Schmid
jhs@gnome.org

# Colección de música

      Neste titorial aprenderá:

        Como conectar con unha base de datos usando libgda
        Como insertar e examinar rexistros nunha táboa dunha base de datos

# Introdución

Esta demostración usa o linguaxe JavaScript. Vaise demostrar como conectar e usar unha base de datos desde un programa GTK, usando a biblioteca GDA («GNOME Data Access», Acceso a datos de GNOME). Polo tanto, é preciso ter instalada tamén esta biblioteca.

GNOME Data Access (GDA) é unha biblioteca cuxo propósito é fornecer acceso universal a diferentes tipos de orixes de datos. Isto vai desde os sistemas de bases de datos relacionais tradicionais a calquera tipo imaxinábel de orixe de datos como un servidor de correo, un directorio LDAP, ect. Para máis información e para obter a API completa e a documentación visite o sitio web de GDA.

Aínda que a maioría do código está relacionado con interfaces de usuario (GUI), imos enforcar este titorial nas partes relacionadas coa base de datos (aínda que podemos mencionar outras partes que son relevantes). Para saber máis sobre os programas JavaScript en GNOME vexa o titorial programa Visor de Imaxes.

# Cree un proxecto de Anjuta

Antes de comezar a programar, deberá configurar un proxecto novo en Anjuta. Isto creará todos os ficheiros que precise para construír e executar o código máis adiante. Tamén é útil para manter todo ordenado.

Inicie Anjuta e prema Ficheiro Novo Proxecto para abrir o asistente de proxectos.

Seleccione JavaScript xenérico desde a lapela JS, prema Adiante e complete os seus detalles nas seguintes páxinas. Use record-collection como nome do proxecto e cartafol.

Prema Rematar e o proxecto será creado. Abra src/main.js desde a lapela Proxecto ou Ficheiros. Contén código de exemplo moi básico.

# Estrutura do programa

Esta demostración é un aplicativo GTK (cunha única xanela) capaz de inserir rexistros nunha táboa de base de datos así como navegar por tódolos rexistros da toa. A táboa ten dous campos: `id`, un enteiro e `name`, un varchar. A primeira sección (na parte superior) do aplicativo permítelle inserir un rexistro na táboa. A última sección (abaixo) permítelle ver tódolos rexistros dunha táboa. Os contidos actualízanse cada vez que se insire un novo rexistro e ao iniciar o aplicativo.

# Comezar a diversión

Comezar examinando o esqueleto do 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 (0, null);

var demo = new Demo ();

Gtk.main ();

Liñas 1-4: importacións iniciais. Preste especial atención á liña 3, que indica que JavaScript importe a a biblioteca GDA, noso obxectivo neste titorial.

Liñas 6-17: Define a clase `Demo`. Poña especial atención ás liñas 13-15, onde chamamos a 3 métodos que farán o traballo. Falaremos deles máis adiante.

Liñas 19-23: Iniciar o aplicativo.

# Deseñar o aplicativo

Botémoslle unha ollada ao método `setupWindow`. É o responsábel de crear a Interface de Usuario (UI). Xa que a UI non é o noso enfoque explicaremos só as partes máis relevantes.

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

Liñas 22 e 27: crear as dúas entradas (para os dous campos) nas que o usuario escribirá algo para inserilo na base de datos.

Liñas 31-34: crear o botón «Insertar». Conéctase o seu sinal `clicked` co método privado `_insertClicked` de clase. Este método detállase a continuación

Liña 39: Crear o widget (`TextView`) onde se mostrarán os contidos da táboa.

Liña 44: crea a etiqueta onde se mostrará o número de rexistros existentes na táboa. Inicialmente está baleiro, actualizarase máis tarde.

# Conectar e inicializar a base de datos

O código que fai a conexión coa base de datos está no método `setupDatabase` de embaixo:

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 = Gda.execute_select_command (this.connection, "select * from demo"); } catch (e) { Gda.execute_non_select_command (this.connection, "create table demo (id integer, name varchar(100))"); } },

Liña 2-3: crear o obxecto de GDA `Conection`. Debe fornecerlle ao seu construtor algunhas propiedades:

`provider`: un dos fornecedores con asistencia en GDA. GDA é compatíbel con SQLite, MySQL, PostgreSQL, Oracle e moitos outros. Para os propósitos desta demostración usaremos unha base de datos SQLite, xa que ven instalada por omisión na maioría das distribucións e é moi sinxela de usar (usa un só ficheiro por base de datos).

`cnc_string`: A cadea de conexión. Pode cambiar dun fornecedor a outro. A sintaxe para SQLite é: `DB_DIR=PATH;DB_NAME=FILENAME`. Nesta demostración accederemos a unha base de datos chamada gnome_demo no cartafol persoal do usuario (olle a chamada á función de GLib `get_home_dir`).

Se o fornecedor non é compatíbel con GDA, ou falta algún elemento na cadea de conexión, dispararase a excepción da liña 2. Polo que na vida real deberíamos xestionar as declaracións JavaScript entre bloques `try`...`catch`.

Liña 4: Abrir a conexión. No fornecedor de SQLite, se a conexión non existe crearase neste paso.

Liñas 6-10: Tenta facer unha selección sinxela para comprobar que a táboa existe (liña 7). Se non existe (porque a base de datos foi recén creada), esta orde disparará unha excepción, que está xestionada polo bloque `try`...`catch`. Se este é o caso executarase a declaración de creación de táboa (liña 9).

Para executar ordes SQL usamos as funcións globais de GDA, `execute_select_command` e `execute_non_select_command`. Son moi sinxelas de usar, e só requiren dous argumentos: o obxecto `Connection` e a orde SQL a analizar.

Neste punto temos a base de datos configurada e lista para usala.

# Seleccionar

After connecting to the database, our demo's constructor calls the `selectData` method. It is responsible for getting all the records in the table and showing them on the `TextView` widget. Let's take a look at it:

selectData: function () { var dm = Gda.execute_select_command (this.connection, "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 global GDA's function `execute_select_command` for that. It returns a `DataModel` object, which is later used to retrieve the rows.

Line 3: Create an `Iter` object, which is used to iterate over the `DataModel`'s records.

Line 7: Loop through all the records, fetching them with the help of the `Iter` object. At this point, the `iter` variable contains the actual, retrieved data. Its `move_next` method returns `false` when it reaches the last record.

Lines 8‒9: We do two things in each line:

Use `Iter`'s method `get_value_at`, which requires only one argument: the column number to retrieve, starting at 0. As our `SELECT` command returns only two columns, we are retrieving columns 0 and 1.

The method `get_value_at` returns the field in GLib's `GValue` format. A simple way to convert this format to a string is by using GDA's global function `value_stringify`. That's what we are doing here, and we store the results in the variables `id_field` and `name_field`.

Line 11: Concatenate the two fields to make one text line, separated by `"=>"`, and store it in the `text` variable.

Line 14: After the loop is finished, we have all the records formatted in the `text` variable. In this line we just set the contents of the `TextView` with that variable.

Line 15: Display the number of records in the table, making use of the `DataModel`'s `get_n_rows` method.

# Inserir

OK, we know how to connect to a database and how to select rows from a table. Now it's time to do an `INSERT` on the table. Do you remember above, in the method `setupWindow` we connected the Insert button's `clicked` signal to the method `_insertClicked`? Let's see the implementation of this method.

_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 GDA's convenience functions `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:

Lines 2‒3: Check if the user filled all the fields. The code for the private method `_validateFields` is really simple and you can read it in the full demo source code.

Line 5: The faster way of doing the `INSERT`. It's commented out as we want to show how to use the `SqlBuilder` object to build a SQL statement portable across databases.

Line 7: Create the `SqlBuilder` object. We must pass the type of statement we are going to build. It can be `SELECT`, `UPDATE`, `INSERT` or `DELETE`.

Line 8: Set the name of the table on which the built statement will operate (it will generate `INSERT INTO demo`)

Lines 9‒10: Set the fields and its values that will be part of the statement. The first argument is the field name (as in the table). The second one is the value for that field.

Line 11: Get the dynamically generated `Statement` object, which represents a SQL statement.

Line 12: Finally, execute the SQL statement (`INSERT`).

Line 14: Clear the id and name fields on the screen. The code for the private method `_clearFields` is really simple and you can read it in the full demo source code.

Line 15: Refresh the view on the screen by doing another `SELECT`.

You can also make use of parameters while building the statement. By using the `SqlBuilder` objects and parameters you are less subject to attacks like SQL injection. Check the GDA documentation for more information about parameters.

# Executar o aplicativo

All of the code you need should now be in place, so try running the code. You now have a database for your record collection!

# Implementación de referencia

If you run into problems with the tutorial, compare your code with this reference code.