[gnome-devel-docs] Added database access demo



commit 0458fac61f8d30ed7cfeff335a6a64c54d085303
Author: Jonh Wendell <jwendell gnome org>
Date:   Sat Dec 4 13:50:19 2010 +0100

    Added database access demo

 demos/database/demo-gda.js |  148 +++++++++++++++++++++++
 demos/database/gda.page    |  284 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 432 insertions(+), 0 deletions(-)
---
diff --git a/demos/database/demo-gda.js b/demos/database/demo-gda.js
new file mode 100644
index 0000000..a624786
--- /dev/null
+++ b/demos/database/demo-gda.js
@@ -0,0 +1,148 @@
+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 ();
+  },
+
+  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 ();
+  },
+
+  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))");
+    }
+  },
+
+  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>";
+  },
+
+  _showError: function (msg) {
+    var dialog = new Gtk.MessageDialog ({message_type: Gtk.MessageType.ERROR,
+                                         buttons: Gtk.ButtonsType.CLOSE,
+                                         text: msg,
+                                         transient_for: this.window,
+                                         modal: true,
+                                         destroy_with_parent: true});
+    dialog.run ();
+    dialog.destroy ();
+  },
+
+  _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 ();
+  },
+
+  _validateFields: function () {
+    if (this.id_entry.text == "" || this.name_entry.text == "") {
+      this._showError ("All fields are mandatory");
+      return false;
+    }
+    if (isNaN (parseInt (this.id_entry.text))) {
+      this._showError ("Enter a number");
+      this.id_entry.grab_focus ();
+      return false;
+    }
+    return true;
+  },
+
+  _clearFields: function () {
+    this.id_entry.text = "";
+    this.name_entry.text = "";
+    this.id_entry.grab_focus ();
+  }
+}
+
+Gtk.init (0, null);
+
+var demo = new Demo ();
+
+Gtk.main ();
diff --git a/demos/database/gda.page b/demos/database/gda.page
new file mode 100644
index 0000000..c2f502a
--- /dev/null
+++ b/demos/database/gda.page
@@ -0,0 +1,284 @@
+<page xmlns="http://projectmallard.org/1.0/";
+      type="topic"
+      id="gda">
+
+  <info>
+    
+    <link type="guide" xref="index"/>
+    
+    <link type="seealso" xref="index"/>
+    
+    <desc>Database Access Demo</desc>
+    
+    <revision pkgversion="0.1" version="0.1" date="2010-12-03" status="stub"/>
+    <credit type="author">
+      <name>GNOME Documentation Project</name>
+      <email>gnome-doc-list gnome org</email>
+    </credit>
+    
+  </info>
+
+<title>Database Access Demo</title>
+
+<synopsis>
+  <p>In this tutorial, you will learn:</p>
+  <list>
+    <item><p>How to connect to a database using libgda</p></item>
+    <item><p>How to insert and browse records in a database table</p></item>
+  </list>
+</synopsis>
+
+<section>
+  <title>Introduction</title>
+  <p>
+    This demo uses the Javascript language. You need the <em>gjs</em> interpreter (already present in GNOME 3). We are going to demonstrate how to connect and use a database from a GTK program, by using the GDA (GNOME Data Access) library. Thus you also need this library installed.
+  </p>
+  <p>
+    GNOME Data Access (GDA) is library whose purpose is to provide universal access to different kinds and types of data sources. This goes from traditional relational database systems, to any imaginable kind of data source such as a mail server, a LDAP directory, etc. For more information, and for a full API and documentation, visit the GDA website.
+  </p>
+  <p>
+    Although a big part of the code is related to user interface (GUI), we are going to focus our tutorial on the database parts (we might mention other parts we think are relevant though). To know more about Javascript programs in GNOME, see the Image Viewer program tutorial in Javascript. LINK.
+  </p>
+  <p>
+    In order to run this demo, just save the code into a file (for instance demo-gda.js) and run the following command:
+  </p>
+  <screen>
+    gjs demo-gda.js
+  </screen>
+</section>
+
+<section>
+  <title>Program Structure</title>
+  <media type="image" mime="image/png" src="demo.png"/>
+  <p>This demo is a simple GTK application (with a single window) capable of insert records into a database table as well browse all records of the table. The table has two fields: id, integer and name, varchar. The first section (on the top) of the application allows you to insert a record into the table. The last section (bottom) allows you to see all the records of that table. Its content is refreshed every time a new record is inserted and on the application startup.
+  </p>
+</section>
+
+<section>
+  <title>Starting the fun</title>
+  <p>Let's start by commenting the skeleton of the program:</p>
+  <code mime="text/javascript" style="numbered"><![CDATA[
+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 ();]]>
+  </code>
+  <list>
+    <item><p>Lines 1-4: Initial imports. Special attention to line 3, which tells Javascript to import the GDA library, our focus in this tutorial.</p></item>
+    <item><p>Lines 6-17: Define our Demo class. Special attention to lines 13-15, where we call 3 methods which will do all the job. They will be detailed below.</p></item>
+    <item><p>Lines 19-23: Start the application.</p></item>
+  </list>
+</section>
+
+<section>
+  <title>Designing the application</title>
+  <p>Let's take a look at <em>setupWindow</em> method. It is responsible for creating the User Interface (UI). As UI is not our focus, let's comment only the relevant parts.</p>
+  <code mime="text/javascript" style="numbered"><![CDATA[
+  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 ();
+  },]]>
+  </code>
+  <list>
+    <item><p>Lines 22 and 27: Create the 2 entries (for the two fields) in which users will type something to get inserted in the database.</p></item>
+    <item><p>Lines 31-34: Create the Insert button. We connect its <em>clicked</em> signal to the <em>_insertClicked</em> private method of the class. This method is detailed below.</p></item>
+    <item><p>Line 39: Create the widget (TextView) where we will show the contents of the table.</p></item>
+    <item><p>Line 44: Create the label where we will show the number of records in the table. Initially it's empty, it will be updated later.</p></item>
+  </list>
+</section>
+
+<section>
+  <title>Connecting to and initializing the database</title>
+  <p>
+     The code which makes the connection to the database is in the <em>setupDatabase</em> method below:
+  </p>
+  <code mime="text/javascript" style="numbered"><![CDATA[
+  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))");
+    }
+  },]]>
+  </code>
+  <list>
+    <item>
+      <p>Lines 2-3: Create the GDA's <em>Connection</em> object. We must supply to its constructor some properties:</p>
+      <list>
+        <item>
+          <p>provider: One of GDA's supported provider. GDA supports SQLite, MySQL, Posgresql, Oracle and many others. For demo purposes we will use a SQLite database, as it comes installed by default in most distributions and it is simple to use (it just uses a file as a database).</p>
+        </item>
+        <item>
+          <p>cnc_string: The connection string. It may change from provider to provider. The syntax for SQLite is: DB_DIR=PATH;DB_NAME=FILENAME. In this demo we are accessing a database called gnome_demo in the user home dir (note the call to GLib's <em>get_home_dir</em> function).</p>
+        </item>
+      </list>
+      <note>
+        <p>If the provider is not supported by GDA, or if the connection string is missing some element, line 2 will raise an exception. So, in real life we should handle it with javascript's statement try...catch.</p>
+      </note>
+    </item>
+
+    <item><p>Line 4: Open the connection. In SQLite provider, if the database does not exist, it will be created in this step.</p></item>
+    <item>
+      <p>Lines 6-10: Try to do a simple select to check if the table exists (line 7). If it does not exist (because the database was just created), this command will raise an exception, which is handled by the try..catch block. If it is the case, we run the create table statement (line 9).</p>
+      <p>In order to run the SQL commands above we are using global GDA functions, <em>execute_select_command</em> and <em>execute_non_select_command</em>. They are simple to use, and just require two arguments: The <em>Connection</em> object and the SQL command to be parsed.</p>
+    </item>
+  </list>
+
+  <p>At this point we have the database set up, and are ready to use it.</p>
+</section>
+
+<section>
+  <title>Selecting</title>
+  <p>
+     After connect to the database, our demo's constructor calls <em>selectData</em> 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:
+  </p>
+  <code mime="text/javascript" style="numbered"><![CDATA[
+  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>";
+  },]]>
+  </code>
+  <list>
+    <item><p>Line 2: The SELECT command. We are using the global GDA's function <em>execute_select_command</em> for that. It returns a <em>DataModel</em> object, which is later used to retrieve the rows.</p></item>
+    <item><p>Line 3: Create an <em>Iter</em> object, which is used to iterate over the <em>DataModel</em>'s records.</p></item>
+    <item><p>Line 7: Loop fetching all the records with the help of the <em>Iter</em> object. At this point, <em>iter</em> variable contains the actual, retrieved data. Its <em>move_next</em> method returns false when it reaches the last record.</p></item>
+    <item>
+      <p>Lines 8-9: We do two things in each line:</p>
+      <list>
+        <item><p>Use Iter's method <em>get_value_at</em>, which requires only one argument: the column number to retrieve, starting on 0. As our SELECT command returns only two columns, we are retrieving columns 0 and 1.</p></item>
+        <item><p>The method <em>get_value_at</em> returns the field in the GLib's <em>GValue</em> format. A simple way to convert this format to a string is by using the GDA's global function <em>value_stringify</em>. That's what we are doing here, and we store the results in the variables <em>id_field</em> and <em>name_field</em>.</p></item>
+      </list>
+    </item>
+    <item><p>Line 11: Concatenate the two fields to make one text line, separated by "=>", and store it in the <em>text</em> variable</p></item>
+    <item><p>Line 14: After the loop is finished, we have all records formatted in the <em>text</em> variable. In this line we just set the contents of the TextView with that variable.</p></item>
+    <item><p>Line 15: Display the number of records in the table, making use of the DataModel's <em>get_n_rows</em> method.</p></item>
+  </list>
+</section>
+
+<section>
+  <title>Inserting</title>
+  <p>
+     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 <em>setupWindow</em> we connected the Insert button clicked signal to the method <em>_insertClicked</em>? Let's see the implementation of this method.
+  </p>
+  <code mime="text/javascript" style="numbered"><![CDATA[
+  _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 ();
+  },]]>
+  </code>
+  <p>
+    We have learned how to use GDA's convenience functions <em>execute_select_command</em> and <em>execute_non_select_command</em> to quickly execute SQL commands on the database. GDA allows one to build a SQL statement indirectly, by using its <em>SqlBuilder</em> object. The benefits of this? GDA will generate dynamically the SQL statement, 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:
+  </p>
+  <list>
+    <item><p>Lines 2-3: Check if the user filled all fields. The code for the private method <em>_validateFields</em> is really simple and you can read it in the full demo source code.</p></item>
+    <item><p>Line 5: The faster way of doing the INSERT. It's commented as we want to show how to use the <em>SqlBuilder</em> object to build a SQL statement portable across databases.</p></item>
+    <item><p>Line 7: Create the <em>SqlBuilder</em> object. We must pass the type of statement we are going to build. It can be SELECT, UPDATE, INSERT or DELETE.</p></item>
+    <item><p>Line 8: Set the name of the table on which the built statement will operate (it will generate <em>INSERT INTO demo</em>)</p></item>
+    <item><p>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.</p></item>
+    <item><p>Line 11: Get the dynamically generated <em>Statement</em> object, which represents a SQL statement.</p></item>
+    <item><p>Line 12: Finally, execute the SQL statement (INSERT).</p></item>
+    <item><p>Line 14: Clear the id and name fields on the screen.  The code for the private method <em>_clearFields</em> is really simple and you can read it in the full demo source code.</p></item>
+    <item><p>Line 15: Refresh the view on the screen by doing another SELECT.</p></item>
+  </list>
+  <note><p>You can also make use of parameters while building the statement. By using the <em>SqlBuilder</em> objects and parameters you are less subject to attacks like SQL injection. Check the GDA documentation for more information about parameters.</p></note>
+</section>
+
+<section>
+  <title>The work continues...</title>
+  <p>
+    Final considerations...
+    You can grab the complete source code of this demo at [LINK].
+  </p>
+</section>
+</page>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]